diff options
51 files changed, 6478 insertions, 0 deletions
diff --git a/plugins/org.eclipse.objectteams.otredyn/.classpath b/plugins/org.eclipse.objectteams.otredyn/.classpath new file mode 100644 index 000000000..64c5e31b7 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/plugins/org.eclipse.objectteams.otredyn/.project b/plugins/org.eclipse.objectteams.otredyn/.project new file mode 100644 index 000000000..e0a35ad69 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>org.eclipse.objectteams.otredyn</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/plugins/org.eclipse.objectteams.otredyn/.settings/org.eclipse.jdt.core.prefs b/plugins/org.eclipse.objectteams.otredyn/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..8871fecd9 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,79 @@ +#Fri Jul 15 18:22:12 CEST 2011 +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.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +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=warning +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.includeNullInfoFromAsserts=disabled +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.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +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.suppressOptionalErrors=disabled +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.unusedObjectAllocation=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 +org.eclipse.objectteams.otdt.compiler.option.pure_java=enabled diff --git a/plugins/org.eclipse.objectteams.otredyn/META-INF/MANIFEST.MF b/plugins/org.eclipse.objectteams.otredyn/META-INF/MANIFEST.MF new file mode 100644 index 000000000..4732fceb7 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/META-INF/MANIFEST.MF @@ -0,0 +1,18 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: %pluginName +Bundle-SymbolicName: org.eclipse.objectteams.otredyn +Bundle-Version: 0.8.0.qualifier +Bundle-Vendor: %providerName +Bundle-Localization: plugin +Export-Package: org.objectteams, + org.eclipse.objectteams.otredyn.bytecode, + org.eclipse.objectteams.otredyn.bytecode.asm, + org.eclipse.objectteams.otredyn.runtime, + org.eclipse.objectteams.otredyn.transformer, + org.eclipse.objectteams.otredyn.transformer.jplis, + org.eclipse.objectteams.otredyn.transformer.names, + org.eclipse.objectteams.otredyn.util +Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Bundle-ClassPath: . +Require-Bundle: org.objectweb.asm;bundle-version="3.3.1" diff --git a/plugins/org.eclipse.objectteams.otredyn/build.properties b/plugins/org.eclipse.objectteams.otredyn/build.properties new file mode 100644 index 000000000..b5e3b75ef --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +bin.includes = META-INF/,\ + .,\ + plugin.properties diff --git a/plugins/org.eclipse.objectteams.otredyn/plugin.properties b/plugins/org.eclipse.objectteams.otredyn/plugin.properties new file mode 100644 index 000000000..f48715fbd --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/plugin.properties @@ -0,0 +1,2 @@ +pluginName=Object Teams Dynamic Runtime Environment +providerName=Eclipse.org - Object Teams diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java new file mode 100644 index 000000000..ff292ad7b --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractBoundClass.java @@ -0,0 +1,1036 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2009, 2012 Oliver Frank and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Oliver Frank - Initial API and implementation + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otredyn.bytecode; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.objectteams.otredyn.runtime.ClassIdentifierProviderFactory; +import org.eclipse.objectteams.otredyn.runtime.IBinding; +import org.eclipse.objectteams.otredyn.runtime.IBoundClass; +import org.eclipse.objectteams.otredyn.runtime.IMethod; +import org.eclipse.objectteams.otredyn.runtime.TeamManager; +import org.eclipse.objectteams.otredyn.runtime.ISubclassWiringTask; +import org.eclipse.objectteams.otredyn.transformer.jplis.ObjectTeamsTransformer; + +import org.objectweb.asm.Opcodes; + +/** + * This class represents a java class. + * It stores the information about a class parsed from the bytecode and + * it handles callin and decapsulation bindings for the class + * @author Oliver Frank + */ +public abstract class AbstractBoundClass implements IBoundClass { + + /** + * A structure that is internally used to store which methods are still woven + * and which has to be woven. + */ + private static class WeavingTask { + public static enum WeavingTaskType { + WEAVE_BINDING_OF_SUBCLASS, + WEAVE_BINDING, + WEAVE_INHERITED_BINDING, + WEAVE_METHOD_ACCESS, + WEAVE_FIELD_ACCESS, + WEAVE_INHERITED_MEMBER_ACCESS + } + + private WeavingTaskType weavingTaskType; + private String memberName; + private String memberSignature; + private boolean doAllTransformations; + private boolean isHandleCovariantReturn; + + public WeavingTask(WeavingTaskType weavingTaskType, String memberName, String memberSignature, boolean handleCovariantReturn) { + this.weavingTaskType = weavingTaskType; + this.memberName = memberName; + this.memberSignature = memberSignature; + this.isHandleCovariantReturn = handleCovariantReturn; + } + + /** + * Returns the type of the WeavingTask + * @return + */ + public WeavingTaskType getType() { + return weavingTaskType; + } + + /** + * Returns the name of the member, that has to be woven + * @return + */ + public String getMemberName() { + return memberName; + } + + /** + * Returns the signature of the member, that has to be woven + * @return + */ + public String getMemberSignature() { + return memberSignature; + } + + /** + * This information is only needed for callin bindings. + * @return If true, all transformation has to be done, if false, + * only callAllBindings has to be redefined + */ + public boolean doAllTransformations() { + return doAllTransformations; + } + + public void setDoAllTransformations(boolean doAllTransformations) { + this.doAllTransformations = doAllTransformations; + } + + public boolean isHandleCovariantReturn() { + return this.isHandleCovariantReturn; + } + } + + // completed WeavingTasks for callin bindings mapped by the method, + // that was woven + private Map<Method, WeavingTask> completedBindingTasks; + + // not completed WeavingTasks for callin bindings mapped by the method, + // that has to be woven + private Map<Method, WeavingTask> openBindingTasks; + + // completed WeavingTasks for decapsulation bindings mapped by the member, + // that was woven + private Map<Member, WeavingTask> completedAccessTasks; + + // open WeavingTasks for decapsulation bindings mapped by the member, + // that has to be woven + private Map<Member, WeavingTask> openAccessTasks; + + private List<ISubclassWiringTask> wiringTasks; + + //FQN (e.g. "foo.bar.MyClass") + private String name; + + //internal FQN (e.g. "foo/bar/MyClass.class") + private String internalName; + + // A globally unique identifier for the class + private String id; + private String superClassName; + private String internalSuperClassName; + private AbstractBoundClass superclass; + private AbstractBoundClass enclosingClass; + private Map<String, Method> methods; + private Map<String, Field> fields; + private Map<AbstractBoundClass, Object> subclasses; + + // Is the java class, that was represented by the AbstractBoundClass + // already loaded by a class loader or not + private boolean isLoaded; + + // Does this AbstractBoundClass represent a team + private boolean isTeam; + private boolean isInterface; + + private int otClassFlags; + private int visibilityFlags; + private boolean implicitTeamActivationEnabled = false; + private Set<String> methodsForImplicitActivation; + + protected ClassLoader loader; + + + /** + * No public constructor, beacause only the ClassRepository should + * create AbstractBoundClasses + * @param name dot-separated class name + * @param id unique identifier, able to differentiate same-named classes from different classloaders + * @param loader classloader responsible for loading this class + */ + protected AbstractBoundClass(String name, String id, ClassLoader loader) { + if (name.indexOf('/')!= -1) + new RuntimeException(name).printStackTrace(System.out); + this.name = name; + this.internalName = name.replace('.', '/'); + this.id = id; + this.loader = loader; + completedBindingTasks = new IdentityHashMap<Method, WeavingTask>(); + openBindingTasks = new IdentityHashMap<Method, WeavingTask>(); + openAccessTasks = new IdentityHashMap<Member, WeavingTask>(); + completedAccessTasks = new IdentityHashMap<Member, WeavingTask>(); + methods = new HashMap<String, Method>(); + fields = new HashMap<String, Field>(); + subclasses = new IdentityHashMap<AbstractBoundClass, Object>(); + + // don't fetch a anonymous subclass for a anonymous subclass + if (!name.equals(ClassRepository.ANONYMOUS_SUBCLASS_NAME)) { + AbstractBoundClass anonymousSubclass = ClassRepository + .getInstance().getAnonymousSubclass(this); + subclasses.put(anonymousSubclass, null); + } + } + + /** + * Returns the name of the Class + * @return + */ + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public boolean isAnonymous() { + return getName().equals(ClassRepository.ANONYMOUS_SUBCLASS_NAME); + } + + /** + * Returns the classloader responsible for loading this class. + * @return + */ + public ClassLoader getClassLoader() { + return this.loader; + } + + /** + * Set the name of the super class of this class + * @param superClassName + */ + public void setSuperClassName(String superClassName) { + if (superClassName == null) { + return; + } + this.superClassName = superClassName.replace('/', '.'); + this.internalSuperClassName = superClassName.replace('.', '/'); + } + + public boolean isJavaLangObject() { + return this.name.equals("java.lang.Object"); + } + + /** + * Is the class a interface? + * @return + */ + public boolean isInterface() { + parseBytecode(); + return isInterface; + } + + /** + * Mark as interface + * @param isInterface + */ + public void setInterface(boolean isInterface) { + this.isInterface = isInterface; + } + + /** + * Does this AbstractBoundClass represents a Team. + * @return if true, the method getTeam of the ClassRepository + * could be called to get an instance of AbstractTeam + */ + public boolean isTeam() { + parseBytecode(); + return isTeam; + } + + /** + * Mark as team + * @param isTeam + */ + public void setTeam(boolean isTeam) { + this.isTeam = isTeam; + } + + public void setOTClassFlags(int flags) { + this.otClassFlags = flags; + } + public void setVisibility(int flags) { + this.visibilityFlags = flags; + } + + public boolean isRole() { + return (this.otClassFlags & Types.ROLE_FLAG) != 0; + } + + public boolean isProtected() { + return (this.visibilityFlags & Opcodes.ACC_PROTECTED) != 0; + } + + public void enableImplicitActivation() { + this.implicitTeamActivationEnabled = true; + } + + public void registerMethodForImplicitActivation(String methodNameAndDesc) { + if (this.methodsForImplicitActivation == null) + this.methodsForImplicitActivation = new HashSet<String>(); + this.methodsForImplicitActivation.add(methodNameAndDesc); + } + + public boolean hasMethodImplicitActivation(String methodNameAndDesc) { + if (this.implicitTeamActivationEnabled) // for all methods + return true; + if (this.methodsForImplicitActivation == null) + return false; + return this.methodsForImplicitActivation.contains(methodNameAndDesc); + } + + /** + * Do all needed transformations needed at load time: + * Add the interface IBoundBase + * Add the empty method callOrig + * Add the empty method callOrigStatic + * Add the empty method access + * Add the empty method accessStatic + * Add the empty method callAllBindings + */ + public void transformAtLoadTime() { + handleTaskList(); + } + + /** + * Mark as loaded + */ + public void setLoaded() { + this.isLoaded = true; + } + + /** + * Returns an instance of AbstractBoundClass that represents + * the super class of this class. + * It parses the bytecode, if that has not already been done + * @return an instance of AbstractBoundClass that represents + * the super class of this class + */ + public synchronized AbstractBoundClass getSuperclass() { + parseBytecode(); + if (superClassName != null && superclass == null) { + String superclassId = ClassIdentifierProviderFactory.getClassIdentifierProvider().getSuperclassIdentifier(id, internalSuperClassName); + + //if superclassId is null the class could be "Object" or an interface + if (superclassId != null) { + superclass = ClassRepository.getInstance().getBoundClass(superClassName, superclassId, loader); + superclass.addSubclass(this); + // FIXME(SH): can we avoid adding all subclasses to j.l.Object? + } + } + return superclass; + } + + /** + * Returns an instance of AbstractBoundClass that represents + * the enclosing class of this class. + * It parses the bytecode, if that has not already been done + * @return an instance of AbstractBoundClass that represents + * the enclosing class of this class + */ + public synchronized AbstractBoundClass getEnclosingClass() { + parseBytecode(); + int pos = this.internalName.lastIndexOf('$'); + if (pos != -1) { + String enclosingClassName = this.internalName.substring(0, pos); + // FIXME(SH): do we need a new getEnclosingClassIdentifier? + String enclosingClassID = ClassIdentifierProviderFactory.getClassIdentifierProvider().getSuperclassIdentifier(id, enclosingClassName); + + if (enclosingClassID != null) { + enclosingClass = ClassRepository.getInstance().getBoundClass(enclosingClassName, enclosingClassID, loader); + enclosingClass.addSubclass(this); + } + } + return enclosingClass; + } + + public synchronized String getEnclosingClassName() { + parseBytecode(); + int pos = this.internalName.lastIndexOf('$'); + if (pos != -1) { + return this.internalName.substring(0, pos); + } + return null; + } + + /** + * This method parses the bytecode, if that has not already been done + */ + protected abstract void parseBytecode(); + + /** + * Returns the internal name of the super class of this class + * It parses the bytecode, if that has not already been done + * @return + */ + protected String getInternalSuperClassName() { + parseBytecode(); + return internalSuperClassName; + } + + /** + * Returns the internal name of this class + * @return + */ + protected String getInternalName() { + return internalName; + } + + /** + * Returns the name of the super class of this class + * It parses the bytecode, if that has not already been done + * @return + */ + public String getSuperClassName() { + parseBytecode(); + return superClassName; + } + + /** + * Adds a method to this class. + * This method is intended to be called, + * while parsing the bytecode + * @param name the name of the field + * @param desc the signature of the method + * @param isStatic is this method static + * @param isPrivate is this method private + */ + public void addMethod(String name, String desc, boolean isStatic, int accessFlags) { + String methodKey = getMethodKey(name, desc); + Method method = methods.get(methodKey); + // Does this method already exists? + // Methods are created by getMethod, if the class is not loaded + if (method == null) { + method = new Method(name, desc, isStatic, accessFlags); + method.setImplemented(true); + methods.put(methodKey, method); + } else { + // Yes, so set additional information. + method.setImplemented(true); + method.setStatic(isStatic); + } + } + + /** Method signature sauf the return type. */ + private String getMethodKey(String name, String desc) { + int pos = desc.indexOf(')'); + return name+desc.substring(0,pos+1); + } + + /** + * Adds a field to this class. + * This method is intended to be called, + * while parsing the bytecode + * @param name the name of the field + * @param desc the signature of the field + * @param isStatic is this field static + * @param accessFlag ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE or 0. + */ + public void addField(String name, String desc, boolean isStatic, int accessFlags) { + Field field = fields.get(name); + if (field == null) { + field = new Field(name, desc, isStatic, accessFlags); + fields.put(name, field); + } else { + field.setStatic(isStatic); + } + } + + public synchronized Method getMethod(String name, String desc, boolean allowCovariantReturn) { + parseBytecode(); + String methodKey = getMethodKey(name, desc); + Method method = methods.get(methodKey); + if (!allowCovariantReturn && method != null && !method.getSignature().equals(desc)) + method = null; // don't use this + if (method == null) { + // class was not yet loaded + method = new Method(name, desc); + methods.put(methodKey, method); + } + return method; + } + + // same as above but specifically request a static/non-static method + public synchronized Method getMethod(String name, String desc, boolean allowCovariantReturn, boolean isStatic) { + parseBytecode(); + String methodKey = getMethodKey(name, desc); + Method method = methods.get(methodKey); + if (!allowCovariantReturn && method != null && !method.getSignature().equals(desc)) + method = null; // don't use this + if (method == null) { + // class was not yet loaded + method = new Method(name, desc); + method.setStatic(isStatic); + methods.put(methodKey, method); + } + assert method.isStatic() == isStatic : "Mismatching static/non-static methods "+getName()+'.'+name+desc; + return method; + } + + public synchronized Field getField(String name, String desc) { + parseBytecode(); + Field field = fields.get(name); + if (field == null) { + // class was not yet loaded + field = new Field(name, desc); + fields.put(name, field); + } + return field; + } + + + public String getMethodIdentifier(IMethod method) { + return getId() + method.getName() + method.getSignature(); + } + + /** + * Adds a subclass to this class + * @param subclass + */ + protected void addSubclass(AbstractBoundClass subclass) { + subclasses.put(subclass, null); + } + + /** + * Remove subclass from this class. It's only needed + * to remove a anonymous subclass, if a real subclass is loaded + * @param subclass + */ + protected void removeSubclass(AbstractBoundClass subclass) { + subclasses.remove(subclass); + } + + /** + * Returns all subclasses of this class, + * including the anonymous subclass + * @return + */ + private Collection<AbstractBoundClass> getSubclasses() { + return subclasses.keySet(); + } + + /** + * Handle all open weaving tasks for this class. + * It redefines the class, if it is not called while loading + */ + public synchronized void handleTaskList() { + Set<Map.Entry<Method, WeavingTask>> bindingEntrySet = openBindingTasks + .entrySet(); + + Set<Map.Entry<Member, WeavingTask>> accessEntrySet = openAccessTasks + .entrySet(); + + // Are there not handled callin or decapsulation bindings + // for this class + if (bindingEntrySet.size() > 0 || accessEntrySet.size() > 0) { + // Yes, so start the transformation, parse the bytecode + // and do load time transforming, if this method is called + // at load time + startTransformation(); + parseBytecode(); + prepareAsPossibleBaseClass(); + prepareTeamActivation(); + } else if (isFirstTransformation()) { + // No, so only do load time transforming, if this method is called + // at load time + startTransformation(); + prepareAsPossibleBaseClass(); + prepareTeamActivation(); + endTransformation(); + } + + for (Map.Entry<Method, WeavingTask> entry : bindingEntrySet) { + WeavingTask task = entry.getValue(); + Method method = entry.getKey(); + switch (task.getType()) { + // Weave callin binding to a method of a subclass, that is not implemented + // in the subclass + case WEAVE_BINDING_OF_SUBCLASS: + // Is the method implemented in this class? + if (method.isImplemented()) { + // Yes, so weave this class + weaveBindingOfSubclass(task); + } else { + //No, so just delegate the weaving task to the superclass + AbstractBoundClass superclass = getSuperclass(); + // If superclass is null, her is something wrong + if (superclass != null) { + superclass.addWeavingTask(task); + weaveSuperCallInCallOrig(task); // ensure we're actually calling that super version + } + } + break; + // Weave callin binding to a method of this class + case WEAVE_BINDING: + if (method.isStatic()) { + weaveBindingInStaticMethod(task); + } else { + // Is the method implemented in this class? + if (method.isImplemented()) { + // So redefine the method + weaveBindingInImplementedMethod(task); + } else { + //No, so weave this class and delegate to the super class + weaveBindingInNotImplementedMethod(task); + AbstractBoundClass superclass = getSuperclass(); + Method superMethod = superclass.getMethod(method.getName(), method.getSignature(), + task.isHandleCovariantReturn()); + WeavingTask newTask = new WeavingTask( + WeavingTask.WeavingTaskType.WEAVE_BINDING_OF_SUBCLASS, + superMethod.getName(), superMethod.getSignature(), + task.isHandleCovariantReturn()); + superclass.addWeavingTask(newTask); + } + +// Original comment: +// If this method is private, the callin binding is not +// inherited by the subclasses +// However, this conflicts with test415_nonexistingBaseMethod3i, +// where an empty callAllBindings() was overriding a non-empty one. +// see also Method.getId() +// if (!method.isPrivate()) { + // Delegate the WeavingTask to the subclasses + for (AbstractBoundClass subclass : getSubclasses()) { + Method subMethod = subclass.getMethod(method.getName(), method.getSignature(), + task.isHandleCovariantReturn()); + WeavingTask newTask = new WeavingTask( + WeavingTask.WeavingTaskType.WEAVE_INHERITED_BINDING, + subMethod.getName(), subMethod.getSignature(), + task.isHandleCovariantReturn()); + subclass.addWeavingTask(newTask); + TeamManager.mergeJoinpoints(this, subclass, method, subMethod, task.isHandleCovariantReturn()); + } +// } + } + break; + // Weave Binding inherited from the superclass + case WEAVE_INHERITED_BINDING: + if (method.isImplemented()) { + weaveBindingInImplementedMethod(task); + } else { + weaveBindingInNotImplementedMethod(task); + } + + // Delegate the WeavingTask to the subclasses + for (AbstractBoundClass subclass : getSubclasses()) { + Method subMethod = subclass.getMethod(method.getName(), method.getSignature(), + task.isHandleCovariantReturn()); + WeavingTask newTask = new WeavingTask( + WeavingTask.WeavingTaskType.WEAVE_INHERITED_BINDING, subMethod + .getName(), subMethod.getSignature(), + task.isHandleCovariantReturn()); + subclass.addWeavingTask(newTask); + TeamManager.mergeJoinpoints(this, subclass, method, subMethod, task.isHandleCovariantReturn()); + } + + break; + } + + // Mark all WeavingTasks for callin bindings + // as completed + completedBindingTasks.put(method, task); + } + + //handle all WeavinTasks for decapsulation bindings + for (Map.Entry<Member, WeavingTask> entry : accessEntrySet) { + WeavingTask task = entry.getValue(); + Member member = entry.getKey(); + + switch (task.getType()) { + // handle decapsulation binding to a field + case WEAVE_FIELD_ACCESS: + prepareForFirstMemberAccess(); + Field field = getField(task.getMemberName(), task + .getMemberSignature()); + weaveFieldAccess(field, field.getId(this)); + if (!field.isStatic()) { + // If the field is not static it could be accessed through a subclass + // so weave the subclass + for (AbstractBoundClass subclass : getSubclasses()) { + WeavingTask newTask = new WeavingTask( + WeavingTask.WeavingTaskType.WEAVE_INHERITED_MEMBER_ACCESS, + null, null, false/*handleCovariantReturn*/); + subclass.addWeavingTask(newTask); + } + } + break; + // handle decaspulation binding to a method + case WEAVE_METHOD_ACCESS: + prepareForFirstMemberAccess(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + weaveMethodAccess(method, method.getId(this)); + if (!method.isStatic()) { + // If the method is not static it could be accessed through a subclass + // so weave the subclass + for (AbstractBoundClass subclass : getSubclasses()) { + WeavingTask newTask = new WeavingTask( + WeavingTask.WeavingTaskType.WEAVE_INHERITED_MEMBER_ACCESS, + null, null, false/*handleCovariantReturn*/); + subclass.addWeavingTask(newTask); + } + } + break; + case WEAVE_INHERITED_MEMBER_ACCESS: + prepareForFirstMemberAccess(); + break; + } + + // Mark all WeavingTasks for decapsulation bindings + // as completed + completedAccessTasks.put(member, task); + } + if (openBindingTasks.size() > 0 || openAccessTasks.size() > 0) { + // Weave the class, if the method is not called at load time + endTransformation(); + } + openBindingTasks.clear(); + openAccessTasks.clear(); + } + + public void handleAddingOfBinding(IBinding binding) { + WeavingTask.WeavingTaskType type = null; + switch (binding.getType()) { + case CALLIN_BINDING: + type = WeavingTask.WeavingTaskType.WEAVE_BINDING; + break; + case FIELD_ACCESS: + type = WeavingTask.WeavingTaskType.WEAVE_FIELD_ACCESS; + break; + case METHOD_ACCESS: + type = WeavingTask.WeavingTaskType.WEAVE_METHOD_ACCESS; + break; + default: + throw new RuntimeException("Unknown binding type: " + + binding.getType().name()); + } + if ( binding.getType() == IBinding.BindingType.CALLIN_BINDING + && !binding.getBoundClass().equals(binding.getDeclaringBaseClassName())) + try { + // need to load the declaring base class outside the transform() callback: + this.loader.loadClass(binding.getDeclaringBaseClassName().replace('/', '.')); + } catch (ClassNotFoundException e) { + throw new NoClassDefFoundError(e.getMessage()); + } + WeavingTask task = new WeavingTask(type, binding.getMemberName(), binding.getMemberSignature(), binding.isHandleCovariantReturn()); + addWeavingTask(task); + } + + private void addWeavingTask(WeavingTask task) { + boolean isNewTask = addWeavingTaskLazy(task); + + if (this.isLoaded && isNewTask) { + handleTaskList(); + } + } + + private boolean addWeavingTaskLazy(WeavingTask task) { + WeavingTask.WeavingTaskType type = task.getType(); + boolean isNewTask = false; + if (type == WeavingTask.WeavingTaskType.WEAVE_BINDING + || type == WeavingTask.WeavingTaskType.WEAVE_BINDING_OF_SUBCLASS + || type == WeavingTask.WeavingTaskType.WEAVE_INHERITED_BINDING) { + isNewTask = addBindingWeavingTask(task); + } else { + isNewTask = addAccessWeavingTask(task); + } + + return isNewTask; + } + + /** + * Handle a new WeavingTask for decapsulation and + * figures out if weaving is needed for this task + * @param task + * @return + */ + private boolean addAccessWeavingTask(WeavingTask task) { + WeavingTask.WeavingTaskType type = task.getType(); + Member member = null; + switch (type) { + case WEAVE_FIELD_ACCESS: + member = getField(task.getMemberName(), task.getMemberSignature()); + break; + case WEAVE_METHOD_ACCESS: + member = getMethod(task.getMemberName(), task.getMemberSignature(), false/*covariantReturn*/); + break; + case WEAVE_INHERITED_MEMBER_ACCESS: + openAccessTasks.put(null, task); + return true; + } + + synchronized (member) { + WeavingTask prevTask = completedAccessTasks.get(member); + // Is there a already completed task for the member? + if (prevTask == null) { + // No, so check the open tasks + prevTask = openAccessTasks.get(member); + } + + // Is there a open task for the member? + if (prevTask == null) { + // No, so weaving is needed + openAccessTasks.put(member, task); + return true; + } else { + //Yes, so weaving is not needed + return false; + } + } + } + + /** + * Handle a new WeavingTask for a callin binding and + * figures out if weaving is needed for this task + * @param task + * @return + */ + private boolean addBindingWeavingTask(WeavingTask task) { + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + synchronized (method) { + WeavingTask prevTask = completedBindingTasks.get(method); + // Is there a already completed task for the method? + if (prevTask == null) { + // No, so check the open tasks + prevTask = openBindingTasks.get(method); + } + + // Is there a open task for the member? + if (prevTask == null) { + //No, so weaving is needed + task.setDoAllTransformations(true); + openBindingTasks.put(method, task); + return true; + } + + switch (prevTask.getType()) { + case WEAVE_BINDING: + return false; + case WEAVE_BINDING_OF_SUBCLASS: + // In this case only the callAllBings was redefined. + if (task.getType() != WeavingTask.WeavingTaskType.WEAVE_BINDING_OF_SUBCLASS) { + // Do the other transformations, if the new WeavingTask is not the same + // as already existing + openBindingTasks.put(method, task); + return true; + } + return false; + case WEAVE_INHERITED_BINDING: + return false; + default: + throw new RuntimeException("Unknown WeavingTaskType: " + + prevTask.getType().name()); + } + } + } + + /** + * Merge tasks of two AbstractBoundClasses (this class and a other). + * This method is called if a currently loaded has to be merged + * with a anonymous subclass + * @param clazz + * @return + */ + protected boolean mergeTasks(AbstractBoundClass clazz) { + boolean isNewTask = false; + for (Map.Entry<Method, WeavingTask> entry : clazz.openBindingTasks + .entrySet()) { + isNewTask |= addWeavingTaskLazy(entry.getValue()); + } + + for (Map.Entry<Member, WeavingTask> entry : clazz.openAccessTasks + .entrySet()) { + isNewTask |= addWeavingTaskLazy(entry.getValue()); + } + + return isNewTask; + } + + private void weaveBindingInStaticMethod(WeavingTask task) { + prepareForFirstStaticTransformation(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + false/*covariantReturn*/); + int joinpointId = TeamManager + .getJoinpointId(getMethodIdentifier(method)); + int boundMethodId = method.getId(this); + + moveCodeToCallOrig(method, boundMethodId); + createDispatchCodeInOrgMethod(method, joinpointId, boundMethodId); + } + + /** + * Do all transformations for a method, that is not implemented + * in this class + * @param task + */ + private void weaveBindingInNotImplementedMethod(WeavingTask task) { + prepareForFirstTransformation(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + int joinpointId = TeamManager + .getJoinpointId(getMethodIdentifier(method)); + int boundMethodId = method.getId(this); + if (task.doAllTransformations()) { + createDispatchCodeInCallAllBindings(joinpointId, boundMethodId); + // TODO(SH): instead of iterating superclasses fetch it from the Binding + boolean isWeavable = true; // weavable unless we find it to be declared in an unweavable super + AbstractBoundClass superClass = getSuperclass(); + while (superClass != null) { + if (superClass.isJavaLangObject()) { + isWeavable = false; + break; + } + Method superMethod = superClass.getMethod(task.getMemberName(), task.getMemberSignature(), task.isHandleCovariantReturn()); + if (superMethod.isImplemented()) { + isWeavable = ObjectTeamsTransformer.isWeavable(superClass.getInternalName()); + break; + } + superClass = superClass.getSuperclass(); + } + if (isWeavable) + createSuperCallInCallOrig(boundMethodId); + else + // can't weave into the declaring class, add an override here: + createCallAllBindingsCallInOrgMethod(method, joinpointId, true/*needToAddMethod*/); + } else { + createDispatchCodeInCallAllBindings(joinpointId, boundMethodId); + } + } + + /** + * While delegating a task from a sub class to the super class, + * ensure that the super version is actually called. + */ + private void weaveSuperCallInCallOrig(WeavingTask task) { + prepareForFirstTransformation(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + int boundMethodId = method.getId(this); + if (task.doAllTransformations()) { + createSuperCallInCallOrig(boundMethodId); + } + } + + /** + * Do all transformations for a method, that is implemented + * in this class + * @param task + */ + private void weaveBindingInImplementedMethod(WeavingTask task) { + prepareForFirstTransformation(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + int joinpointId = TeamManager + .getJoinpointId(getMethodIdentifier(method)); + int boundMethodId = method.getId(this); + if (task.doAllTransformations()) { + moveCodeToCallOrig(method, boundMethodId); + createDispatchCodeInCallAllBindings(joinpointId, boundMethodId); + createCallAllBindingsCallInOrgMethod(method, boundMethodId, false); + } else { + createDispatchCodeInCallAllBindings(joinpointId, joinpointId); + } + } + + /** + * Do all transformations for a method, that is bound + * but not implemented in a subclass + * @param task + */ + private void weaveBindingOfSubclass(WeavingTask task) { + prepareForFirstTransformation(); + Method method = getMethod(task.getMemberName(), task.getMemberSignature(), + task.isHandleCovariantReturn()); + int boundMethodId = method.getId(this); + moveCodeToCallOrig(method, boundMethodId); + createCallAllBindingsCallInOrgMethod(method, boundMethodId, false); + + } + + @Override + public String toString() { + return this.name+"["+this.id+"]"; + } + + // See AsmBoundClass or AsmWritableBoundClass for documentation + + protected abstract void startTransformation(); + + protected abstract void endTransformation(); + + protected abstract void prepareAsPossibleBaseClass(); + + protected abstract void prepareTeamActivation(); + + protected abstract void createSuperCallInCallOrig(int boundMethodId); + + protected abstract void createCallAllBindingsCallInOrgMethod( + Method boundMethod, int joinpointId, boolean needToAddMethod); + + protected abstract void createDispatchCodeInCallAllBindings( + int joinpointId, int boundMethodId); + + protected abstract void moveCodeToCallOrig(Method boundMethod, int boundMethodId); + + protected abstract void prepareForFirstTransformation(); + + protected abstract void prepareForFirstStaticTransformation(); + + protected abstract boolean isFirstTransformation(); + + protected abstract void createDispatchCodeInOrgMethod(Method boundMethod, + int joinpointId, int boundMethodId); + + protected abstract void prepareForFirstMemberAccess(); + + protected abstract void weaveFieldAccess(Field field, int accessId); + + protected abstract void weaveMethodAccess(Method method, int accessId); + + public abstract boolean isTransformationActive(); + + public abstract byte[] getBytecode(); + + public void dump(byte[] classfileBuffer, String postfix) {} + + public abstract int compare(String callinLabel1, String callinLabel2); + + public void addWiringTask(ISubclassWiringTask wiringTask) { + if (this.wiringTasks == null) + this.wiringTasks = new ArrayList<ISubclassWiringTask>(); + this.wiringTasks.add(wiringTask); + } + + public void performWiringTasks(AbstractBoundClass superclass, AbstractBoundClass subclass) { + if (this.wiringTasks == null) + return; + synchronized (this.wiringTasks) { + for (ISubclassWiringTask task : this.wiringTasks) + task.wire(superclass, subclass); + this.wiringTasks.clear(); + } + } + +} diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java new file mode 100644 index 000000000..31504ca0a --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/AbstractTeam.java @@ -0,0 +1,76 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.objectteams.otredyn.runtime.IBoundTeam;
+
+/**
+ * Represents a team class and stores the bindings this team has got.
+ */
+public abstract class AbstractTeam extends AbstractBoundClass implements IBoundTeam {
+ /**
+ * All relevant bindings (callin and decapsulation) of this team.
+ * By internally using a TreeSet the set of bindings is naturally sorted
+ * based on {@link Binding#compareTo(Binding)} (see there).
+ */
+ private Set<Binding> bindings;
+ private int highestAccessId;
+
+ protected AbstractTeam(String name, String id, ClassLoader loader) {
+ super(name, id, loader);
+ bindings = new TreeSet<Binding>();
+ }
+
+ /**
+ * Adds a binding to the team.
+ * This method is intended to be called,
+ * while parsing the bytecode
+ * @param binding
+ */
+ public void addBinding(Binding binding) {
+ bindings.add(binding);
+ }
+
+ public Collection<Binding> getBindings() {
+ parseBytecode();
+ return bindings;
+ }
+
+ /**
+ * Returns the superclass of this team as AbstractTeam
+ */
+ @Override
+ public AbstractTeam getSuperclass() {
+ return (AbstractTeam) super.getSuperclass();
+ }
+
+ /**
+ * Record that this team uses the given accessId.
+ * @param accessId
+ */
+ public void recordAccessId(int accessId) {
+ this.highestAccessId = Math.max(this.highestAccessId, accessId);
+ }
+
+ public int getHighestAccessId() {
+ return highestAccessId;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Binding.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Binding.java new file mode 100644 index 000000000..852e1760c --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Binding.java @@ -0,0 +1,165 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import org.eclipse.objectteams.otredyn.runtime.IBinding;
+
+/**
+ * This class represents a callin or decapsulation binding
+ * @author Oliver Frank
+ */
+public class Binding implements Comparable<Binding>, IBinding {
+
+ /** The callin modifier 'before' */
+ public static final int BEFORE = 1;
+ /** The callin modifier 'replace' */
+ public static final int REPLACE = 2;
+ /** The callin modifier 'after' */
+ public static final int AFTER = 3;
+
+ AbstractBoundClass teamClass;
+ private String callinLabel;
+ private String boundClass;
+ private String memberName;
+ private String memberSignature;
+ private String declaringBaseClassName;
+ private int callinModifier;
+ private int callinId;
+ private IBinding.BindingType type;
+ private boolean isHandleCovariantReturn;
+
+ public IBinding.BindingType getType() {
+ return type;
+ }
+
+ /**
+ * Create a callin binding.
+ */
+ public Binding(AbstractBoundClass teamClass,
+ String roleClassName, String callinLabel, String boundClassName,
+ String memberName, String memberSignature, String declaringBaseClassName,
+ int callinModifier, int callinId, boolean handleCovariantReturn)
+ {
+ this.teamClass = teamClass;
+ this.callinLabel = callinLabel;
+ this.boundClass = boundClassName;
+ this.memberName = memberName;
+ this.memberSignature = memberSignature;
+ this.declaringBaseClassName = declaringBaseClassName;
+ this.callinModifier = callinModifier;
+ this.callinId = callinId;
+ this.type = IBinding.BindingType.CALLIN_BINDING;
+ this.isHandleCovariantReturn = handleCovariantReturn;
+ }
+
+ /**
+ * Create a method or field access binding (decapsulation).
+ */
+ public Binding(AbstractBoundClass teamClass,
+ String boundClassName,
+ String memberName, String memberSignature, int callinId, IBinding.BindingType type)
+ {
+ this.teamClass = teamClass;
+ this.boundClass = boundClassName;
+ this.memberName = memberName;
+ this.memberSignature = memberSignature;
+ this.callinId = callinId;
+ this.type = type;
+ }
+
+ public String getBoundClass() {
+ return boundClass;
+ }
+
+ public String getMemberName() {
+ return memberName;
+ }
+
+ public String getMemberSignature() {
+ return memberSignature;
+ }
+
+ public int getCallinId() {
+ return callinId;
+ }
+
+ public boolean isHandleCovariantReturn() {
+ return this.isHandleCovariantReturn;
+ }
+
+ public String getDeclaringBaseClassName() {
+ return this.declaringBaseClassName;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ Binding other = (Binding) obj;
+ return boundClass.equals(other.boundClass)
+ && memberName.equals(other.memberName)
+ && memberSignature.equals(other.memberSignature)
+ && type == other.type && callinId == other.callinId;
+ }
+
+ public int compareTo(Binding other) {
+ // ordering strategy for callin bindings:
+ // - first criterion: callinModifier: before/after have higher priority than replace.
+ // - second criterion: precedence (only relevant among callins of the same callin modifier).
+ // the set AbstractTeam.bindings is sorted low-to-high.
+ // then TeamManager.handleTeamStateChange processes all bindings from low-to-high
+ // inserting each at the front of the list of active teams, such that last added
+ // will indeed have highest priority, which is in line with ordering by activation time.
+ int compare = 0;
+ if (this.callinLabel == null || other.callinLabel == null) {
+ // at least one binding is a decaps binding
+ if (this.callinLabel != null)
+ return 1;
+ else if (other.callinLabel != null)
+ return -1;
+ } else {
+ if (this.callinModifier != other.callinModifier) {
+ // replace has lower priority than before/after:
+ if (this.callinModifier == REPLACE)
+ return -1;
+ else if (other.callinModifier == REPLACE)
+ return 1;
+ }
+ // the following comparison respects precedence:
+ compare = this.teamClass.compare(this.callinLabel, other.callinLabel);
+ if (compare != 0)
+ return compare;
+ }
+ return (boundClass + memberName + memberSignature).compareTo(other.boundClass + other.memberName + other.memberSignature);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ switch (this.type) {
+ case CALLIN_BINDING: buf.append("callin: ");break;
+ case METHOD_ACCESS: buf.append("callout: ");break;
+ case FIELD_ACCESS: buf.append("callout-to-field: ");break;
+ }
+ buf.append('{');
+ buf.append(this.callinId);
+ buf.append("} ");
+ buf.append(this.boundClass);
+ buf.append('.');
+ buf.append(this.memberName);
+ buf.append(this.memberSignature);
+ return buf.toString();
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/BytecodeProviderFactory.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/BytecodeProviderFactory.java new file mode 100644 index 000000000..6f9a2b693 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/BytecodeProviderFactory.java @@ -0,0 +1,35 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+/**
+ * This class creates and provides a singleton instance of
+ * IBytecodeProvider.
+ * Actually it returns a instance of InMemeoryBytecodeProvider.
+ * In the future, this class should decides (e.g. by a system property),
+ * which BytecodeProvider should be used
+ * @author Oliver Frank
+ */
+public class BytecodeProviderFactory {
+ private static IBytecodeProvider instance;
+ public static synchronized IBytecodeProvider getBytecodeProvider() {
+ if (instance == null) {
+ instance = new InMemoryBytecodeProvider();
+ }
+ return instance;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/ClassRepository.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/ClassRepository.java new file mode 100644 index 000000000..48bff216d --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/ClassRepository.java @@ -0,0 +1,193 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2009, 2012 Oliver Frank and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Oliver Frank - Initial API and implementation + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otredyn.bytecode; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; + +import org.eclipse.objectteams.otredyn.bytecode.asm.AsmClassRepository; +import org.eclipse.objectteams.otredyn.runtime.IClassRepository; +import org.eclipse.objectteams.otredyn.runtime.TeamManager; + +/** + * This class creates and provides instances of + * AbstractBoundClass and AbstractTeam. It is the only + * instance that should do this. It is a singelton, thats + * why the constructor should not called directly. + * The instance is provided by getInstance. + * @author Oliver Frank + */ +public abstract class ClassRepository implements IClassRepository { + private static ClassRepository instance; + + protected ClassRepository() { + + } + + /** + * Returns a singleton instance of the ClassRepository. + * @return + */ + public static synchronized ClassRepository getInstance() { + if (instance == null) { + instance = new AsmClassRepository(); + TeamManager.setup(instance); + } + + return instance; + } + + protected static final String ANONYMOUS_SUBCLASS_NAME = "AnonymousSubclass"; + + private Map<String, AbstractTeam> boundClassMap = new HashMap<String, AbstractTeam>(); + private Map<AbstractBoundClass, AbstractBoundClass> anonymousSubclassMap = new IdentityHashMap<AbstractBoundClass, AbstractBoundClass>(); + + /** + * Returns a instance of AbstractBoundClass for the + * given FQN and id. If there is no instance it + * is created. The class have not to be loaded to get + * an instance of AbstractBoundClass representing this class. + * It guarantees, that it returns always the same + * instance for the same id. More formally: + * if id1.equals(id2) then getBoundClass(..., id1) == getBoundClass(id2) + * @param className the name of the class + * @param id a globally unique identifier for the class + * @return + */ + public synchronized AbstractBoundClass getBoundClass(String className, String id, ClassLoader loader) { + AbstractTeam clazz = boundClassMap.get(id); + if (clazz == null) { + clazz = createClass(className, id, BytecodeProviderFactory.getBytecodeProvider(), loader); + boundClassMap.put(id, clazz); + } + + return clazz; + } + + /** + * Returns a instance of AbstractBoundClass for the + * given FQN and id and sets the bytecode for this class. + * If there is no instance it is created. + * This class should be called while loading the class. + * It guarantees, that it returns always the same + * instance for the same id. More formally: + * if id1.equals(id2) then getBoundClass(..., id1) == getBoundClass(..., id2) + * @param className the name of the class + * @param id a globally unique identifier for the class + * @return + */ + public synchronized AbstractBoundClass getBoundClass(String className, String id, byte[] classBytes, ClassLoader loader) + { + AbstractTeam clazz = boundClassMap.get(id); + // set the bytecode in the BytecodeProvider + IBytecodeProvider bytecodeProvider = BytecodeProviderFactory.getBytecodeProvider(); + bytecodeProvider.setBytecode(id, classBytes); + if (clazz == null) { + clazz = createClass(className, id, bytecodeProvider, loader); + } + + boundClassMap.put(id, clazz); + + clazz.setLoaded(); + + return clazz; + } + + /** + * This method links a class with its superclass. + * It checks if the superclass was already loaded. + * If it was already loaded it merges this class with + * the anonymous subclass of its superclass + * @param clazz the subclass + */ + public void linkClassWithSuperclass(AbstractBoundClass clazz) { + // FIXME(SH): also link with tsuper classes?? + AbstractBoundClass superclass = clazz.getSuperclass(); + AbstractBoundClass anonymousSubclass = null; + if (superclass != null) { + anonymousSubclass = anonymousSubclassMap.get(superclass); + } + + //Is there an anonmous subclass, that corresponds with this class + if (anonymousSubclass != null) { + //Yes, so merge the tasks + AbstractBoundClass newAnonymousSubclass = createClass(ANONYMOUS_SUBCLASS_NAME, + ANONYMOUS_SUBCLASS_NAME, + BytecodeProviderFactory.getBytecodeProvider(), + clazz.getClassLoader()); + superclass.removeSubclass(anonymousSubclass); + superclass.addSubclass(newAnonymousSubclass); + superclass.addSubclass(clazz); + newAnonymousSubclass.mergeTasks(anonymousSubclass); + clazz.mergeTasks(anonymousSubclass); + anonymousSubclass.performWiringTasks(superclass, clazz); + anonymousSubclassMap.put(superclass, newAnonymousSubclass); + } + } + + /** + * Returns a instance of AbstractTeam for the + * given FQN and id. If there is no instance it + * is created. + * It guarantees, that it returns always the same + * instance for the same id. More formally: + * if id1.equals(id2) then getTeam(..., id1) == getTeam(..., id2). + * This method should only be called, if it is known + * that the name identifies a team and not a class. + * Otherwise call getBoundClass().isTeam() to check this. + * @param teamName the name of the team + * @param id a globally unique identifier for the team + * @return + */ + public AbstractTeam getTeam(String teamName, String id, ClassLoader loader) { + return (AbstractTeam) getBoundClass(teamName, id, loader); + } + + /** + * Returns a instance of AbstractBoundClass representing + * a anonymous subclass. A anonymous subclass stands for all subclasses, + * that are not known yet and stores the WeavingTasks inherited + * from the superclass. This class could not have got direct bindings. + * If a real subclass of its superclass is loaded all WeavingTasks are + * merged with the real subclass + * @param abstractBoundClass + * @return + */ + protected AbstractBoundClass getAnonymousSubclass( + AbstractBoundClass abstractBoundClass) { + AbstractBoundClass anonymousSubclass = anonymousSubclassMap.get(abstractBoundClass); + if (anonymousSubclass == null) { + anonymousSubclass = createClass(ANONYMOUS_SUBCLASS_NAME, + ANONYMOUS_SUBCLASS_NAME, + BytecodeProviderFactory.getBytecodeProvider(), + abstractBoundClass.getClassLoader()); + anonymousSubclass.setSuperClassName(abstractBoundClass.getName()); + anonymousSubclassMap.put((AbstractTeam) abstractBoundClass, anonymousSubclass); + } + return anonymousSubclass; + } + + /** + * Creates a instance of AbstractBoundClass and AbstracTeam + * @param name + * @param id + * @param bytecodeProvider + * @return + */ + protected abstract AbstractTeam createClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader); +} diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Field.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Field.java new file mode 100644 index 000000000..617d20983 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Field.java @@ -0,0 +1,46 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import org.eclipse.objectteams.otredyn.runtime.IBoundClass;
+
+/**
+ * Represents a field of a class
+ * @author Oliver Frank
+ */
+public class Field extends Member {
+
+ public Field(String name, String signature) {
+ super(name, signature);
+ }
+
+ public Field(String name, String signature, boolean isStatic, int accessFlags) {
+ super(name, signature, isStatic, accessFlags);
+ }
+
+ /**
+ * Returns a globally unique id for the field
+ * @param clazz
+ * @return
+ */
+ @Override
+ public int getId(IBoundClass clazz) {
+ String key = null;
+ key = clazz.getId() + getName() + getSignature();
+ return getId(key);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IBytecodeProvider.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IBytecodeProvider.java new file mode 100644 index 000000000..216a1f434 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IBytecodeProvider.java @@ -0,0 +1,43 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+/**
+ * Interface for classes, that provide bytecode for other classes
+ * @author Oliver Frank
+ */
+public interface IBytecodeProvider {
+ /**
+ * Returns the bytecode of the class with the given id.
+ * Attention: This may be an expensive operation
+ * (e.g. if the bytecode must be read from the hard disk).
+ * @param classId
+ * @return the bytecode of the class or null, if the class
+ * was not loaded yet.
+ */
+ public byte[] getBytecode(String classId);
+
+ /**
+ * Sets the bytecode of the class with the given id
+ * in the BytecodeProvider
+ * Attention: This may be an expensive operation
+ * (e.g. if the bytecode is written to the hard disk).
+ * @param classId
+ * @param bytecode
+ */
+ public void setBytecode(String classId, byte[] bytecode);
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IRedefineStrategy.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IRedefineStrategy.java new file mode 100644 index 000000000..fd06f876e --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/IRedefineStrategy.java @@ -0,0 +1,35 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import java.lang.instrument.UnmodifiableClassException;
+
+/**
+ * Classes that implements this interface can redefine classes at runtime.
+ * @author Oliver Frank
+ */
+public interface IRedefineStrategy {
+
+ /**
+ * Redefines a class at runtime.
+ * @param clazz the existing class instance
+ * @param bytecode the new bytecode for this class
+ * @throws ClassNotFoundException the class was not loaded yet
+ * @throws UnmodifiableClassException it is impossible to redefine the class
+ */
+ public void redefine(Class<?> clazz, byte[] bytecode) throws ClassNotFoundException, UnmodifiableClassException;
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/InMemoryBytecodeProvider.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/InMemoryBytecodeProvider.java new file mode 100644 index 000000000..57e9bb706 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/InMemoryBytecodeProvider.java @@ -0,0 +1,51 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is an implementaion of IBytecodeProvider
+ * It keeps the bytecode of all classes in memory.
+ * @author Oliver Frank
+ */
+public class InMemoryBytecodeProvider implements IBytecodeProvider {
+
+ private Map<String, byte[]> bytecodeMap = new HashMap<String, byte[]>();
+
+ /**
+ * Returns the bytecode of a class with the given id.
+ * In this implementation, this is not an expensive operation.
+ * @return the bytecode of the class or null, if the class
+ * was not loaded yet.
+ */
+ public byte[] getBytecode(String className) {
+ return bytecodeMap.get(className);
+ }
+
+ /**
+ * Sets the bytecode of the class with the given id
+ * in the BytecodeProvider
+ * In this implementation, this is not an expensive operation.
+ * @param classId
+ * @param bytecode
+ */
+ public void setBytecode(String className, byte[] bytecode) {
+ bytecodeMap.put(className, bytecode);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Member.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Member.java new file mode 100644 index 000000000..a017daf01 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Member.java @@ -0,0 +1,95 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.objectteams.otredyn.runtime.IBoundClass;
+import org.eclipse.objectteams.otredyn.runtime.IMember;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Represents a member of a class.
+ * This class abstract. Instances of this class can only be created
+ * as {@link Field} or {@link Method}.
+ * @author Oliver Frank
+ */
+public abstract class Member implements IMember {
+
+ private static Map<String, Integer> idMap = new HashMap<String, Integer>();
+ private static int currentId = 0;
+
+ private String name;
+ private String signature;
+ private boolean isStatic;
+ private int accessFlags;
+
+ public Member(String name, String signature) {
+ this.name = name;
+ this.signature = signature;
+ }
+
+ public Member(String name, String signature, boolean isStatic, int accessFlags) {
+ this(name, signature);
+ this.isStatic = isStatic;
+ this.accessFlags = accessFlags;
+ }
+
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ public void setStatic(boolean isStatic) {
+ this.isStatic = isStatic;
+ }
+
+ public boolean isPrivate() {
+ return (this.accessFlags & Opcodes.ACC_PRIVATE) != 0;
+ }
+
+ public int getAccessFlags() {
+ return this.accessFlags;
+ }
+
+ /**
+ * Returns a globally unique id for a given key.
+ * @param key
+ * @return
+ */
+ protected int getId(String key) {
+ Integer id = idMap.get(key);
+ if (id == null) {
+ synchronized (idMap) {
+ idMap.put(key, currentId);
+ return currentId++;
+ }
+ }
+
+ return id;
+ }
+
+ public abstract int getId(IBoundClass clazz);
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Method.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Method.java new file mode 100644 index 000000000..b988f511a --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Method.java @@ -0,0 +1,64 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2009, 2012 Oliver Frank and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Oliver Frank - Initial API and implementation + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otredyn.bytecode; + +import org.eclipse.objectteams.otredyn.runtime.IBoundClass; +import org.eclipse.objectteams.otredyn.runtime.IMethod; + + +/** + * + */ +public class Method extends Member implements IMethod { + + private boolean implemented; + + public Method(String name, String signature) { + super(name, signature); + } + + public Method(String name, String signature, boolean isStatic, int accessFlags) { + super(name, signature, isStatic, accessFlags); + } + + public boolean isImplemented() { + return implemented; + } + + public void setImplemented(boolean implemented) { + this.implemented = implemented; + } + + /** + * Returns a globally unique id for the method + * @param clazz + * @return + */ + @Override + public int getId(IBoundClass clazz) { + String key = null; + // special treatment of private conflicts with test415_nonexistingBaseMethod3i + // see also AbstractBoundClass.handleTaskList() + if (/*isPrivate() ||*/ isStatic()) { + key = clazz.getId() + getName() + getSignature(); + } else { + key = getName() + getSignature(); + } + + return getId(key); + } +} diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java new file mode 100644 index 000000000..7f854cf7e --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/OtreRedefineStrategy.java @@ -0,0 +1,36 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.UnmodifiableClassException;
+
+import org.eclipse.objectteams.otredyn.transformer.jplis.otreAgent;
+
+/**
+ * This implementation of {@link IRedefineStrategy} uses the
+ * JPLIS agent {@link otreAgent} to redefine classes.
+ * @author Oliver Frank
+ */
+public class OtreRedefineStrategy implements IRedefineStrategy {
+
+ public void redefine(Class<?> clazz, byte[] bytecode) throws ClassNotFoundException, UnmodifiableClassException {
+ ClassDefinition arr_cd[] = { new ClassDefinition(clazz, bytecode) };
+ otreAgent.getInstrumentation().redefineClasses(arr_cd);
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/RedefineStrategyFactory.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/RedefineStrategyFactory.java new file mode 100644 index 000000000..93860b028 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/RedefineStrategyFactory.java @@ -0,0 +1,37 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+/**
+ * Provides a singleton instance of {@link IRedefineStrategy}.
+ * If no instance was set, this class returns the {@link OtreRedefineStrategy}
+ * @author Oliver Frank
+ */
+public class RedefineStrategyFactory {
+ private static IRedefineStrategy redefineStrategy;
+
+ public static IRedefineStrategy getRedefineStrategy() {
+ if (redefineStrategy == null) {
+ redefineStrategy = new OtreRedefineStrategy();
+ }
+ return redefineStrategy;
+ }
+
+ public static void setRedefineStrategy(IRedefineStrategy redefineStrategy) {
+ RedefineStrategyFactory.redefineStrategy = redefineStrategy;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Types.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Types.java new file mode 100644 index 000000000..ed2435bb1 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/Types.java @@ -0,0 +1,139 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode;
+
+/**
+ * Helper class to handle type strings used in the bytecode
+ * @author Oliver Frank
+ */
+public abstract class Types {
+ public final static int ACCESS_PACKAGE = 0; // class, field, method
+ public final static int ACCESS_PUBLIC = 1; // class, field, method
+ public final static int ACCESS_PRIVATE = 2; // class, field, method
+ public final static int ACCESS_PROTECTED = 4; // class, field, method
+ public final static int ACCESS_STATIC = 8; // field, method
+ public final static int TEAM = 0x8000;
+ public final static int ROLE_FLAG = 2; // within OTClassFlags attribute
+
+ public static final String VOID = "V";
+
+ public static final String BOOLEAN = "Z";
+
+ public static final String CHAR = "C";
+
+ public static final String BYTE = "B";
+
+ public static final String SHORT = "S";
+
+ public static final String INT = "I";
+
+ public static final String FLOAT = "F";
+
+ public static final String LONG = "J";
+
+ public static final String DOUBLE = "D";
+
+ private static final String ARRAY = "[";
+
+ public static String getAsArrayType(String className) {
+ if (className.length() == 1) {
+ return ARRAY + className;
+ }
+
+ return ARRAY + "L" + className;
+ }
+
+ public static String getAsType(String type) {
+ return "L" + type;
+ }
+
+ public static String getAsType(Class<?> clazz) {
+ String type = null;
+ if (clazz.isPrimitive()) {
+ String name = clazz.getName();
+ if (name.compareTo("void") == 0) {
+ type = VOID;
+ } else if (name.compareTo("boolean") == 0) {
+ type = BOOLEAN;
+ } else if (name.compareTo("char") == 0) {
+ type = CHAR;
+ } else if (name.compareTo("byte") == 0) {
+ type = BYTE;
+ } else if (name.compareTo("short") == 0) {
+ type = SHORT;
+ } else if (name.compareTo("int") == 0) {
+ type = INT;
+ } else if (name.compareTo("float") == 0) {
+ type = FLOAT;
+ } else if (name.compareTo("long") == 0) {
+ type = LONG;
+ } else if (name.compareTo("double") == 0) {
+ type = DOUBLE;
+ }
+ } else {
+ type = clazz.getName().replace('.', '/');
+ if (!clazz.isArray()) {
+ type = "L" + type;
+ }
+ }
+
+ return type;
+ }
+
+ public static String getTypeStringForMethod(String returnType, String[] paramTypes) {
+ String result = "(";
+ if (paramTypes != null) {
+ for (String paramType : paramTypes) {
+ result += paramType;
+ if (paramType.length() > 2) {
+ result += ";";
+ }
+ }
+ }
+ result += ")" + returnType;
+ if (returnType.length() > 2) {
+ result += ";";
+ }
+ return result;
+ }
+
+ public static String getTypeStringForMethod(Class<?> returnType, Class<?>[] paramTypes) {
+ String result = "(";
+ if (paramTypes != null) {
+ for (Class<?> paramType : paramTypes) {
+ String paramTypeString = getAsType(paramType);
+ result += paramTypeString;
+ if (!paramType.isPrimitive() && !paramType.isArray()) {
+ result += ";";
+ }
+ }
+ }
+ String returnTypeString = getAsType(returnType);
+
+ result += ")" + returnTypeString;
+ if (!returnType.isPrimitive() && !returnType.isArray()) {
+ result += ";";
+ }
+ return result;
+ }
+
+ public static String getTypeStringForField(String type) {
+ return type + ";";
+ }
+
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractCreateDispatchCodeAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractCreateDispatchCodeAdapter.java new file mode 100644 index 000000000..449222e98 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractCreateDispatchCodeAdapter.java @@ -0,0 +1,154 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * Create the code for the dispatch from a base class to the teams. <br/> <br/>
+ * The code was generated as follows: <br/>
+ * <code>
+ * switch (boundMethodId) { // this was generated in CreateSwitchAdapter <br/>
+ * ... <br/>
+ * case (...): // this was generated in concrete implementations <br/>
+ * // of this abstract class <br/>
+ * Teams[] teams = TeamManager.getTeams(joinpointId) <br/><br/>
+ * if (teams == null) { <br/>
+ * break; <br/>
+ * } <br/>
+ * <br/>
+ * Team t = teams[0]; <br/>
+ * int[] callinIds = TeamManager.getCallinIds(joinpointId); <br/>
+ * Object[] args = {arg1, ... , argn};
+ * return team._OT$callAllBindings(this, teams, 0, callinIds, boundMethodId, args);
+ * } <br/>
+ * </code>
+ * @author Oliver Frank
+ */
+public abstract class AbstractCreateDispatchCodeAdapter extends
+ AbstractTransformableClassNode {
+
+ private boolean isStatic;
+
+ public AbstractCreateDispatchCodeAdapter(boolean isStatic) {
+ this.isStatic = isStatic;
+ }
+
+ private Type[] args;
+
+ protected InsnList getDispatchCode(MethodNode method, int joinPointId,
+ int boundMethodId) {
+ InsnList instructions = new InsnList();
+
+ // teams = TeamManager.getTeams(joinpointId)
+ instructions.add(createLoadIntConstant(joinPointId));
+
+ instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
+ ClassNames.TEAM_MANAGER_SLASH, ConstantMembers.getTeams.getName(),
+ ConstantMembers.getTeams.getSignature()));
+
+ instructions.add(createInstructionsToCheackTeams(method));
+
+ // get the first team
+ instructions.add(new InsnNode(Opcodes.DUP));
+ instructions.add(new InsnNode(Opcodes.ICONST_0));
+ instructions.add(new InsnNode(Opcodes.AALOAD));
+ instructions.add(new InsnNode(Opcodes.SWAP));
+ if (isStatic) {
+ instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ } else {
+ // put "this" on the stack and cast it to IBoundBase
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST,
+ ClassNames.I_BOUND_BASE_SLASH));
+ }
+ instructions.add(new InsnNode(Opcodes.SWAP));
+ // start index
+ instructions.add(new InsnNode(Opcodes.ICONST_0));
+
+ // TeamManager.getCallinIds(joinpointId)
+ instructions.add(createLoadIntConstant(joinPointId));
+
+ instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC,
+ ClassNames.TEAM_MANAGER_SLASH,
+ ConstantMembers.getCallinIds.getName(),
+ ConstantMembers.getCallinIds.getSignature()));
+
+ instructions.add(createLoadIntConstant(boundMethodId));
+ args = Type.getArgumentTypes(method.desc);
+
+ // box the arguments
+ instructions.add(getBoxedArguments(args));
+
+ instructions.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
+ ClassNames.ITEAM_SLASH, ConstantMembers.callAllBindingsTeam
+ .getName(), ConstantMembers.callAllBindingsTeam
+ .getSignature()));
+
+ Type returnType = Type.getReturnType(method.desc);
+ instructions.add(getUnboxingInstructionsForReturnValue(returnType));
+
+ return instructions;
+ }
+
+ protected InsnList createInstructionsToCheackTeams(MethodNode method) {
+ // if (teams == null) {
+ // break;
+ // }
+ InsnList instructions = new InsnList();
+ instructions.add(new InsnNode(Opcodes.DUP));
+ LabelNode label = new LabelNode();
+ instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, label));
+ instructions.add(new InsnNode(Opcodes.POP));
+ instructions.add(new JumpInsnNode(Opcodes.GOTO, findBreakLabel(method.instructions)));
+ instructions.add(label);
+ return instructions;
+ }
+
+ private LabelNode findBreakLabel(InsnList instructions) {
+ for (int i = instructions.size() - 1; i >= 0; i--) {
+ AbstractInsnNode node = instructions.get(i);
+ if (node.getType() == AbstractInsnNode.LABEL) {
+ return (LabelNode) node;
+ }
+ }
+ throw new RuntimeException("Can't find break label to create dispatch code");
+ }
+
+ protected abstract InsnList getBoxedArguments(Type[] args);
+
+ protected int getMaxLocals() {
+ return args.length + 1;
+ }
+
+ protected int getMaxStack() {
+ return 10;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java new file mode 100644 index 000000000..e964657a4 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AbstractTransformableClassNode.java @@ -0,0 +1,272 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * Every class, that wants to manipulate the bytecode of a class
+ * with the ASM Trea API, have to inherit from this class and do
+ * the transformations in the method transform().
+ * Additionally the class provides util methods to
+ * manipulate the bytecode
+ * @author Oliver Frank
+ */
+public abstract class AbstractTransformableClassNode extends ClassNode {
+
+ /**
+ * Returns instructions, that are needed to pack all arguments of a method
+ * in an {@link Object} Array
+ * @param args The Types of the arguments
+ * @param isStatic is this method static or not
+ * @return
+ */
+ protected InsnList getBoxingInstructions(Type[] args, boolean isStatic) {
+ int firstArgIndex = 1;
+ if (isStatic) {
+ firstArgIndex = 0;
+ }
+ InsnList instructions = new InsnList();
+ instructions.add(createLoadIntConstant(args.length));
+ instructions.add(new TypeInsnNode(Opcodes.ANEWARRAY,
+ ClassNames.OBJECT_SLASH));
+ for (int i=0, slot=0; i < args.length; slot += args[i++].getSize()) {
+ instructions.add(new InsnNode(Opcodes.DUP));
+ instructions.add(createLoadIntConstant(i));
+ instructions.add(new IntInsnNode(args[i].getOpcode(Opcodes.ILOAD),
+ slot + firstArgIndex));
+ if (args[i].getSort() != Type.OBJECT
+ && args[i].getSort() != Type.ARRAY) {
+ instructions.add(AsmTypeHelper
+ .getBoxingInstructionForType(args[i]));
+ }
+ instructions.add(new InsnNode(Opcodes.AASTORE));
+ }
+
+ return instructions;
+ }
+
+ /**
+ * Returns the instructions, that are needed to convert
+ * a return value of the type {@link Object} to the real type
+ * @param returnType the real type
+ * @return
+ */
+ protected InsnList getUnboxingInstructionsForReturnValue(Type returnType) {
+ InsnList instructions = new InsnList();
+ switch (returnType.getSort()) {
+ case Type.VOID:
+ instructions.add(new InsnNode(Opcodes.POP));
+ instructions.add(new InsnNode(Opcodes.RETURN));
+ break;
+ case Type.ARRAY: // fallthrough
+ case Type.OBJECT:
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, returnType
+ .getInternalName()));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ break;
+ default:
+ String objectType = AsmTypeHelper.getObjectType(returnType);
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
+ instructions.add(AsmTypeHelper.getUnboxingInstructionForType(
+ returnType, objectType));
+ instructions
+ .add(new InsnNode(returnType.getOpcode(Opcodes.IRETURN)));
+ }
+ return instructions;
+ }
+
+ /**
+ * Adds a new Label to an existing switch statement
+ * @param instructions the instructions, in which the switch statement is defined
+ * @param newInstructions the instructions of the new label
+ * @param labelIndex the index of the label
+ */
+ @SuppressWarnings("unchecked")
+ protected void addNewLabelToSwitch(InsnList instructions,
+ InsnList newInstructions, int labelIndex) {
+ ListIterator<AbstractInsnNode> iter = instructions.iterator();
+ LookupSwitchInsnNode lSwitch = null;
+ while (iter.hasNext()) {
+ AbstractInsnNode node = (AbstractInsnNode) iter.next();
+ if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
+ lSwitch = (LookupSwitchInsnNode) node;
+ LabelNode label = new LabelNode();
+ boolean labelAdded = false;
+ for (int i = 0; i < lSwitch.keys.size(); i++) {
+ Integer key = (Integer) lSwitch.keys.get(i);
+ if (key >= labelIndex) {
+ lSwitch.keys.add(i, labelIndex);
+ lSwitch.labels.add(i, label);
+ labelAdded = true;
+ break;
+ }
+ }
+ if (!labelAdded) {
+ lSwitch.labels.add(label);
+ lSwitch.keys.add(labelIndex);
+ }
+ boolean foundDefLabel = false;
+ AbstractInsnNode prevNode = node;
+ while (iter.hasNext()) {
+ node = (AbstractInsnNode) iter.next();
+ if (node.getType() == AbstractInsnNode.LABEL) {
+ if (!foundDefLabel) {
+ foundDefLabel = true;
+ } else {
+ break;
+ }
+ }
+ prevNode = node;
+ }
+ instructions.insert(prevNode, label);
+ instructions.insert(label, newInstructions);
+ }
+ }
+ if (lSwitch == null) {
+ throw new RuntimeException("No switch statement found.");
+ }
+ }
+
+ /**
+ * Returns a {@link MethodNode} for a given {@link Method} instance
+ * @param method
+ * @return the {@link MethodNode} or null if there is no such method
+ */
+ @SuppressWarnings("unchecked")
+ protected MethodNode getMethod(Method method) {
+ List<MethodNode> methodList = methods;
+ for (MethodNode methodNode : methodList) {
+ if (methodNode.name.compareTo(method.getName()) == 0
+ && methodNode.desc.compareTo(method.getSignature()) == 0) {
+ return methodNode;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * This method could be used to generate debug outputs in the generated code in the form: <br>
+ * <code>
+ * Sytsem.out.println(message);
+ * </code>
+ * @param message
+ * @return
+ */
+ protected InsnList getInstructionsForDebugOutput(String message) {
+ InsnList instructions = new InsnList();
+ instructions.add(new FieldInsnNode(Opcodes.GETSTATIC,
+ "java/lang/System", "out", "Ljava/io/PrintStream;"));
+ instructions.add(new LdcInsnNode(message));
+ instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
+ "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));
+ return instructions;
+ }
+
+ /**
+ * Adds instructions to put all arguments of a method on the stack.
+ * @param instructions
+ * @param args
+ * @param isStatic
+ */
+ protected void addInstructionsForLoadArguments(InsnList instructions, Type[] args, boolean isStatic) {
+ int firstArgIndex = 1;
+ if (isStatic) {
+ firstArgIndex = 0;
+ }
+ // put "this" on the stack for an non-static method
+ if (!isStatic) {
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ }
+ for (int i=0, slot=firstArgIndex; i < args.length; slot+=args[i++].getSize()) {
+ instructions.add(new IntInsnNode(args[i].getOpcode(Opcodes.ILOAD),
+ slot));
+ }
+ }
+
+ /**
+ * Replace all return statements in the given instructions with new
+ * statements that convert the real return value to {@link Object}
+ * and return this new {@link Object}
+ *
+ * @param instructions
+ * @param returnType
+ */
+ @SuppressWarnings("unchecked")
+ protected void replaceReturn(InsnList instructions, Type returnType) {
+ if (returnType.getSort() != Type.OBJECT &&
+ returnType.getSort() != Type.ARRAY &&
+ returnType.getSort() != Type.VOID) {
+ ListIterator<AbstractInsnNode> orgMethodIter = instructions.iterator();
+ while (orgMethodIter.hasNext()) {
+ AbstractInsnNode orgMethodNode = orgMethodIter.next();
+ if (orgMethodNode.getOpcode() == returnType.getOpcode(Opcodes.IRETURN)) {
+ instructions.remove(orgMethodNode);
+ instructions.add(AsmTypeHelper.getBoxingInstructionForType(returnType));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+ }
+ } else if (returnType.getSort() == Type.VOID) {
+ ListIterator<AbstractInsnNode> orgMethodIter = instructions.iterator();
+ while (orgMethodIter.hasNext()) {
+ AbstractInsnNode orgMethodNode = orgMethodIter.next();
+ if (orgMethodNode.getOpcode() == Opcodes.RETURN) {
+ instructions.remove(orgMethodNode);
+ }
+ }
+ instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+ }
+
+ /**
+ * Create an instruction for loading an integer constant,
+ * using the most compact possible format.
+ */
+ protected AbstractInsnNode createLoadIntConstant(int constant) {
+ if (constant <= 5)
+ return new InsnNode(Opcodes.ICONST_0+constant);
+ else if (constant < Byte.MAX_VALUE)
+ return new IntInsnNode(Opcodes.BIPUSH, constant);
+ else
+ return new LdcInsnNode(constant);
+ }
+ /**
+ * In this method, concrete Implementations of this class
+ * can manipulate the bytecode
+ */
+ protected abstract void transform();
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddEmptyMethodAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddEmptyMethodAdapter.java new file mode 100644 index 000000000..56a2a5566 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddEmptyMethodAdapter.java @@ -0,0 +1,95 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This class adds an method only with a return statement
+ * to the bytecode of a class with the ASM Core API
+ * @author Oliver Frank
+ */
+class AddEmptyMethodAdapter extends ClassAdapter {
+
+ private int access;
+ private String name;
+ private String desc;
+ private String signature;
+ private String[] exceptions;
+ private int maxLocals;
+
+ public AddEmptyMethodAdapter(ClassVisitor cv, String name, int access,
+ String desc, String[] exceptions, String signature,
+ int maxLocals) {
+ super(cv);
+ this.access = access;
+ this.desc = desc;
+ this.exceptions = exceptions;
+ this.name = name;
+ this.signature = signature;
+ this.maxLocals = maxLocals;
+ }
+
+ @Override
+ public void visitEnd() {
+ MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
+ if ((this.access & Opcodes.ACC_ABSTRACT) != 0) {
+ mv.visitEnd();
+ return;
+ }
+ mv.visitCode();
+ Type returnType = Type.getReturnType(this.desc);
+ switch (returnType.getSort()) {
+ case Type.VOID:
+ mv.visitInsn(Opcodes.RETURN);
+ break;
+ case Type.INT:
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ mv.visitInsn(Opcodes.ICONST_1);
+ mv.visitInsn(Opcodes.IRETURN);
+ break;
+ case Type.FLOAT:
+ mv.visitInsn(Opcodes.FCONST_1);
+ mv.visitInsn(Opcodes.FRETURN);
+ break;
+ case Type.LONG:
+ mv.visitInsn(Opcodes.LCONST_1);
+ mv.visitInsn(Opcodes.LRETURN);
+ break;
+ case Type.DOUBLE:
+ case Type.OBJECT:
+ case Type.ARRAY:
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ mv.visitInsn(Opcodes.ARETURN);
+ break;
+ }
+ mv.visitMaxs(1, maxLocals);
+ mv.visitEnd();
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int arg0, String arg1, String arg2, String arg3, String[] arg4) {
+ return null; // also consider other visitors
+ }
+}
\ No newline at end of file diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddFieldAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddFieldAdapter.java new file mode 100644 index 000000000..cd1141e35 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddFieldAdapter.java @@ -0,0 +1,52 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Adds an field to the bytecode of a class
+ * @author Oliver Frank
+ */
+class AddFieldAdapter extends ClassAdapter {
+
+ private int access;
+ private String name;
+ private String desc;
+
+ public AddFieldAdapter(ClassVisitor cv, String name, int access,
+ String desc) {
+ super(cv);
+ this.access = access;
+ this.desc = desc;
+ this.name = name;
+ }
+
+ @Override
+ public void visitEnd() {
+ FieldVisitor fv = cv.visitField(access, name, desc, null, null);
+ fv.visitEnd();
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int arg0, String arg1, String arg2, String arg3, String[] arg4) {
+ return null; // also consider other visitors
+ }
+}
\ No newline at end of file diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddInterfaceAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddInterfaceAdapter.java new file mode 100644 index 000000000..7115028a2 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AddInterfaceAdapter.java @@ -0,0 +1,49 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Adds an interface to the bytecode of a class
+ * @author Oliver Frank
+ *
+ */
+public class AddInterfaceAdapter extends ClassAdapter{
+ private String interfaceName;
+
+ public AddInterfaceAdapter(ClassVisitor cv, String interfaceName) {
+ super(cv);
+ this.interfaceName = interfaceName;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+ String[] newInterfaces = new String[interfaces.length + 1];
+ System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
+ newInterfaces[interfaces.length] = interfaceName;
+ super.visit(version, access, name, signature, superName, newInterfaces);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int arg0, String arg1, String arg2, String arg3, String[] arg4) {
+ return null; // also consider other visitors
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmBoundClass.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmBoundClass.java new file mode 100644 index 000000000..31afa82d3 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmBoundClass.java @@ -0,0 +1,163 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
+import org.eclipse.objectteams.otredyn.bytecode.AbstractTeam;
+import org.eclipse.objectteams.otredyn.bytecode.Binding;
+import org.eclipse.objectteams.otredyn.bytecode.IBytecodeProvider;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * This class implements the bytecode parsing for {@link AbstractBoundClass}.
+ * It parses the bytecode with ASM.
+ * @author Oliver Frank
+ */
+abstract class AsmBoundClass extends AbstractTeam {
+
+ private boolean parsed;
+ private IBytecodeProvider bytecodeProvider;
+
+ /**
+ * just a temporary cache for the bytecode
+ */
+ private byte[] bytecode;
+
+ /**
+ * ordered list of qualified callin labels
+ */
+ public String[] precedences;
+
+ protected AsmBoundClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader) {
+ super(name, id, loader);
+ this.bytecodeProvider = bytecodeProvider;
+ }
+
+ /**
+ * Parses the bytecode of a class and uses the set/add... Methods (e.g. addMethod)
+ * of {@link AbstractBoundClass} to set the information
+ */
+ @Override
+ protected synchronized void parseBytecode() {
+ if (parsed) {
+ // Already parsed, nothing to do
+ return;
+ }
+
+ bytecode = bytecodeProvider.getBytecode(getId());
+ if (bytecode == null) {
+ //Class is not loaded yet.
+ return;
+ }
+
+ // Don't parse another time
+ parsed = true;
+ AsmClassVisitor cv = new AsmClassVisitor(this);
+ ClassReader cr = null;
+ cr = new ClassReader(bytecode);
+
+ cr.accept(cv, Attributes.attributes, 0);
+
+ //release the bytecode
+ bytecode = null;
+ }
+
+ /**
+ * Returns the bytecode of this class and cache it temporary.
+ * This method is only needed, if getBytecode of the {@link IBytecodeProvider}
+ * is an expensive operation.
+ * @return
+ */
+ protected byte[] allocateAndGetBytecode() {
+ if (bytecode == null) {
+ bytecode = getBytecode();
+ }
+ return bytecode;
+ }
+
+ /**
+ * Get the bytecode directly from the {@link IBytecodeProvider}.
+ * This method can be used, if getBytecode of the {@link IBytecodeProvider}
+ * is not an expensive operation.
+ * @return
+ */
+ @Override
+ public byte[] getBytecode() {
+ return bytecodeProvider.getBytecode(getId());
+ }
+
+ /**
+ * Releases the bytecode, if it's cached, an set it in the {@link IBytecodeProvider}
+ */
+ protected void releaseBytecode() {
+ bytecodeProvider.setBytecode(getId(), bytecode);
+ bytecode = null;
+ }
+
+ /**
+ * Returns the {@link IBytecodeProvider} used for this class
+ * @return
+ */
+ protected IBytecodeProvider getBytecodeProvider() {
+ return bytecodeProvider;
+ }
+
+ /**
+ * Sets the bytecode.
+ * If the bytecode is temporary cached, the cache is used.
+ * Otherwise this method give the bytecode directly to the {@link IBytecodeProvider}
+ * @param bytecode
+ */
+ protected void setBytecode(byte[] bytecode) {
+ //Is the bytecode temporary cached?
+ if (this.bytecode == null) {
+ // no, then save the bytecode directly in the bytecode provider
+ bytecodeProvider.setBytecode(getId(), bytecode);
+ } else {
+ // yes, then use the cache
+ this.bytecode = bytecode;
+ }
+ }
+
+ public int compare(String callinLabel1, String callinLabel2) {
+ boolean label1Seen = false, label2Seen = false;
+ if (this.precedences != null) {
+ for (String label : this.precedences) {
+ if (label.equals(callinLabel1)) {
+ if (label2Seen)
+ return -1; // saw two then one: one has lower priority than two
+ label1Seen = true;
+ } else if (label.equals(callinLabel2)) {
+ if (label1Seen)
+ return 1; // saw one then two: one has higher priority than two
+ label2Seen = true;
+ }
+ }
+ }
+ AbstractBoundClass enclosingClass = getEnclosingClass();
+ if (enclosingClass != null) {
+ String singleName = getInternalName();
+ int pos = singleName.lastIndexOf('$');
+ singleName = singleName.substring(pos+1);
+ if (singleName.startsWith("__OT__"))
+ singleName = singleName.substring("__OT__".length());
+ // check for precedence at outer level:
+ return enclosingClass.compare(singleName+'.'+callinLabel1, singleName+'.'+callinLabel2);
+ }
+ return callinLabel1.compareTo(callinLabel2);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassRepository.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassRepository.java new file mode 100644 index 000000000..2b062ad17 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassRepository.java @@ -0,0 +1,35 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.AbstractTeam;
+import org.eclipse.objectteams.otredyn.bytecode.ClassRepository;
+import org.eclipse.objectteams.otredyn.bytecode.IBytecodeProvider;
+
+
+/**
+ * Creates Instances of {@link AsmWritableBoundClass} as {@link AbstractTeam}
+ * @author Oliver Frank
+ */
+public class AsmClassRepository extends ClassRepository {
+
+
+ @Override
+ protected AbstractTeam createClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader) {
+ return new AsmWritableBoundClass(name, id, bytecodeProvider, loader);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassVisitor.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassVisitor.java new file mode 100644 index 000000000..a54d4205c --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmClassVisitor.java @@ -0,0 +1,130 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Binding;
+import org.eclipse.objectteams.otredyn.bytecode.Types;
+import org.eclipse.objectteams.otredyn.bytecode.asm.Attributes.CallinBindingsAttribute;
+import org.eclipse.objectteams.otredyn.bytecode.asm.Attributes.OTClassFlagsAttribute;
+import org.eclipse.objectteams.otredyn.bytecode.asm.Attributes.CallinBindingsAttribute.MultiBinding;
+import org.eclipse.objectteams.otredyn.bytecode.asm.Attributes.CallinPrecedenceAttribute;
+import org.eclipse.objectteams.otredyn.bytecode.asm.Attributes.OTSpecialAccessAttribute;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * This class is used to parse the bytecode of a class.
+ * It sets the informations, that are parsed, in the {@link AsmBoundClass}
+ * @author Oliver Frank
+ */
+class AsmClassVisitor extends EmptyVisitor {
+
+ private AsmBoundClass clazz;
+
+ public AsmClassVisitor(AsmBoundClass clazz) {
+ this.clazz = clazz;
+ }
+
+ /**
+ * Parses common information about the class.
+ */
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ clazz.setSuperClassName(superName);
+ clazz.setTeam((access & Types.TEAM) != 0);
+ clazz.setInterface((access & Opcodes.ACC_INTERFACE) != 0);
+ clazz.setVisibility((access & (Opcodes.ACC_PRIVATE|Opcodes.ACC_PROTECTED|Opcodes.ACC_PUBLIC)));
+ }
+
+ /**
+ * Parses the methods of the class
+ */
+ @Override
+ public MethodVisitor visitMethod(int access, final String name, final String desc, String signature, String[] exceptions) {
+ clazz.addMethod(name, desc, (access & Opcodes.ACC_STATIC) != 0, (access & (Opcodes.ACC_PUBLIC|Opcodes.ACC_PROTECTED|Opcodes.ACC_PRIVATE)));
+ if (clazz.isTeam() || clazz.isRole())
+ // check for method annotation ImplicitTeamActivation:
+ return new EmptyVisitor() {
+ @Override
+ public AnnotationVisitor visitAnnotation(String annDesc, boolean visible) {
+ if (annDesc.equals(AddImplicitActivationAdapter.ANNOTATION_IMPLICIT_ACTIVATION))
+ clazz.registerMethodForImplicitActivation(name+desc);
+ return super.visitAnnotation(annDesc, visible);
+ }
+ };
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ /**
+ * Parses the fields of the class
+ */
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ clazz.addField(name, desc, (access & Opcodes.ACC_STATIC) != 0, (access & (Opcodes.ACC_PUBLIC|Opcodes.ACC_PROTECTED|Opcodes.ACC_PRIVATE)));
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ /**
+ * Parses the class file attributes of a class.
+ * This is only needed, if the class is a team.
+ */
+ @Override
+ public void visitAttribute(Attribute attribute) {
+ if (attribute.type.equals(Attributes.ATTRIBUTE_OT_DYN_CALLIN_BINDINGS)) {
+ CallinBindingsAttribute attr = (CallinBindingsAttribute) attribute;
+ MultiBinding[] multiBindings = attr.getBindings();
+ for (int i=multiBindings.length-1; i>=0; i--) { // reverse loop to ensure proper overwriting:
+ String roleClassName = multiBindings[i].getRoleClassName();
+ String callinLabel = multiBindings[i].getCallinLabel();
+ String baseClassName = multiBindings[i].getBaseClassName();
+ String[] baseMethodNames = multiBindings[i].getBaseMethodNames();
+ String[] baseMethodSignatures = multiBindings[i].getBaseMethodSignatures();
+ String[] declaringBaseClassNames = multiBindings[i].getDeclaringBaseClassName();
+ int callinModifier = multiBindings[i].getCallinModifier();
+ int[] callinIds = multiBindings[i].getCallinIds();
+ boolean handleCovariantReturn = multiBindings[i].isHandleCovariantReturn();
+ for (int j = 0; j < baseMethodNames.length; j++) {
+ Binding binding = new Binding(clazz, roleClassName, callinLabel, baseClassName,
+ baseMethodNames[j], baseMethodSignatures[j], declaringBaseClassNames[j],
+ callinModifier, callinIds[j], handleCovariantReturn);
+ clazz.addBinding(binding);
+ }
+ }
+ } else if (attribute.type.equals(Attributes.ATTRIBUTE_CALLIN_PRECEDENCE)) {
+ CallinPrecedenceAttribute attr = (CallinPrecedenceAttribute)attribute;
+ clazz.precedences = attr.labels;
+ } else if (attribute.type.equals(Attributes.ATTRIBUTE_OT_CLASS_FLAGS)) {
+ clazz.setOTClassFlags(((OTClassFlagsAttribute)attribute).flags);
+ } else if (attribute.type.equals(Attributes.ATTRIBUTE_OT_SPECIAL_ACCESS)) {
+ ((OTSpecialAccessAttribute)attribute).registerAt(clazz);
+ }
+ }
+
+ /**
+ * check for class annotation ImplicitTeamActivation:
+ */
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (desc.equals(AddImplicitActivationAdapter.ANNOTATION_IMPLICIT_ACTIVATION))
+ clazz.enableImplicitActivation();
+ return super.visitAnnotation(desc, visible);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java new file mode 100644 index 000000000..5af508dfc --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmTypeHelper.java @@ -0,0 +1,79 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+
+/**
+ * Asm specific helper class to handle type strings used in the bytecode
+ * @author Oliver Frank
+ */
+class AsmTypeHelper {
+ public static AbstractInsnNode getUnboxingInstructionForType(Type primitiveType, String objectType) {
+ String methodName = primitiveType.getClassName() + "Value";
+ String desc = Type.getMethodDescriptor(primitiveType, new Type[] {});
+
+ return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, objectType, methodName, desc);
+ }
+
+ public static String getObjectType(Type primitiveType) {
+ String className = null;
+ switch (primitiveType.getSort()) {
+ case Type.BOOLEAN:
+ className = "Boolean";
+ break;
+ case Type.BYTE:
+ className = "Byte";
+ break;
+ case Type.CHAR:
+ className = "Character";
+ break;
+ case Type.DOUBLE:
+ className = "Double";
+ break;
+ case Type.FLOAT:
+ className = "Float";
+ break;
+ case Type.INT:
+ className = "Integer";
+ break;
+ case Type.LONG:
+ className = "Long";
+ break;
+ case Type.SHORT:
+ className = "Short";
+ break;
+ default:
+ return null;
+ }
+ className = "java/lang/" + className;
+ return className;
+ }
+
+ public static AbstractInsnNode getBoxingInstructionForType(Type type) {
+ String className = getObjectType(type);
+ if (className == null)
+ return new InsnNode(Opcodes.NOP);
+
+ String desc = Type.getMethodDescriptor(Type.getObjectType(className), new Type[] {type});
+ return new MethodInsnNode(Opcodes.INVOKESTATIC, className, "valueOf", desc);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java new file mode 100644 index 000000000..694334489 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/AsmWritableBoundClass.java @@ -0,0 +1,432 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
+import org.eclipse.objectteams.otredyn.bytecode.Field;
+import org.eclipse.objectteams.otredyn.bytecode.IBytecodeProvider;
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.bytecode.RedefineStrategyFactory;
+import org.eclipse.objectteams.otredyn.bytecode.Types;
+import org.eclipse.objectteams.otredyn.runtime.TeamManager;
+import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This class implements the bytecode manipulating part of {@link AbstractBoundClass}.
+ * It uses ASM to manipulate the bytecode.
+ * @author Oliver Frank
+ */
+class AsmWritableBoundClass extends AsmBoundClass {
+ private static boolean dumping = false;
+
+ static {
+ if(System.getProperty("ot.dump")!=null)
+ dumping = true;
+ }
+
+ private ClassWriter writer;
+ private MultiClassAdapter multiAdapter;
+ private ClassReader reader;
+ private boolean isTransformed;
+ private boolean isTransformedForMemberAccess;
+ private boolean isTransformedStatic;
+ private List<AbstractTransformableClassNode> nodes;
+ private boolean isFirstTransformation = true;
+
+ private boolean isTransformationActive;
+
+ protected AsmWritableBoundClass(String name, String id, IBytecodeProvider bytecodeProvider, ClassLoader loader) {
+ super(name, id, bytecodeProvider, loader);
+ }
+
+ /**
+ * Adds a field to the class
+ *
+ * @param field defines name and type of the field
+ * @param access access flags for the field
+ * @see AddFieldAdapter
+ */
+ private void addField(Field field, int access) {
+ assert (isTransformationActive) : "No transformation active";
+ String desc = field.getSignature();
+ multiAdapter.addVisitor(new AddFieldAdapter(writer, field.getName(), access, desc));
+ }
+
+ /**
+ * Adds an empty method to the class
+ * @param method
+ * @param access
+ * @param signature
+ * @param exceptions
+ * @see AddEmptyMethodAdapter
+ */
+ private void addEmptyMethod(Method method, int access, String signature, String[] exceptions) {
+ assert (isTransformationActive) : "No transformation active";
+ String desc = method.getSignature();
+ Type[] args = Type.getArgumentTypes(desc);
+ multiAdapter.addVisitor(new AddEmptyMethodAdapter(writer, method.getName(),
+ access, desc, exceptions, signature, args.length + 1));
+ }
+
+ /**
+ * Adds an interface to the class
+ * @see AddInterfaceAdapter
+ * @param name
+ */
+ private void addInterface(String name) {
+ assert (isTransformationActive) : "No transformation active";
+ multiAdapter.setToplevelVisitor(new AddInterfaceAdapter(writer, name));
+ }
+
+ /**
+ * This method must be called before any transformation
+ * can be done. It makes it possible to collect all transformations
+ * and transform the bytecode only one time at the end.
+ */
+ @Override
+ protected void startTransformation() {
+
+ reader = new ClassReader(allocateAndGetBytecode());
+
+ writer = new ClassWriter(reader, 0);
+ multiAdapter = new MultiClassAdapter(writer);
+ nodes = new ArrayList<AbstractTransformableClassNode>();
+ isTransformationActive = true;
+ }
+
+ /**
+ * Is the class manipulated right now.
+ */
+ @Override
+ public boolean isTransformationActive() {
+ return isTransformationActive;
+ }
+
+ /**
+ * Executes all pending transformations.
+ */
+ @Override
+ protected void endTransformation() {
+ assert (isTransformationActive) : "No transformation active";
+ // //TODO (ofra): Do everything in one transformation
+ // Do all transformation with the Core API of ASM
+ reader.accept(multiAdapter, ClassReader.SKIP_FRAMES);
+ setBytecode(writer.toByteArray());
+ //Do all transformations with the Tree API of ASM
+ for (AbstractTransformableClassNode node : nodes) {
+ reader = new ClassReader(allocateAndGetBytecode());
+ reader.accept(node, ClassReader.SKIP_FRAMES);
+ node.transform();
+ writer = new ClassWriter(reader, 0);
+ node.accept(writer);
+ setBytecode(writer.toByteArray());
+ }
+
+ dump();
+ reader = null;
+ writer = null;
+ multiAdapter = null;
+ nodes = null;
+ //Check, if this is the first transformation for this class
+ if (!this.isFirstTransformation) {
+ // It is not the first transformation, so redefine the class
+ try {
+ redefine();
+ } catch (Throwable t) {
+// t.printStackTrace(System.out);
+ // if redefinition failed (ClassCircularity?) install a runnable for deferred redefinition:
+ final Runnable previousTask = TeamManager.pendingTasks.get();
+ TeamManager.pendingTasks.set(new Runnable() {
+ public void run() {
+ if (previousTask != null)
+ previousTask.run();
+ redefine();
+ }
+ @Override
+ public String toString() {
+ return "Retry "+AsmWritableBoundClass.this.toString();
+ }
+ });
+ }
+ }
+ isTransformationActive = false;
+ isFirstTransformation = false;
+ releaseBytecode();
+ }
+
+ /**
+ * Creates the dispatch code in the original method.
+ * @see CreateDispatchCodeInOrgMethodAdapter
+ */
+ @Override
+ protected void createDispatchCodeInOrgMethod(Method boundMethod,
+ int joinPointId, int boundMethodId) {
+ assert (isTransformationActive) : "No transformation active";
+ nodes.add(new CreateDispatchCodeInOrgMethodAdapter(boundMethod,
+ joinPointId, boundMethodId));
+ }
+
+ /**
+ * Creates the dispatch code in the method callAllBindings.
+ * @see CreateDispatchCodeInCallAllBindingsAdapter
+ */
+ @Override
+ protected void createDispatchCodeInCallAllBindings(int joinpointId,
+ int boundMethodId) {
+ assert (isTransformationActive) : "No transformation active";
+ nodes.add(new CreateDispatchCodeInCallAllBindingsAdapter(joinpointId,
+ boundMethodId));
+ }
+
+ /**
+ * Moves the code of the original method to callOrig or callOrigStatic.
+ * @see MoveCodeToCallOrigAdapter
+ */
+ @Override
+ protected void moveCodeToCallOrig(Method boundMethod, int boundMethodId) {
+ assert (isTransformationActive) : "No transformation active";
+ nodes.add(new MoveCodeToCallOrigAdapter(this, boundMethod, boundMethodId));
+ }
+
+ /**
+ * Creates a super call in callOrig.
+ * @see CreateSuperCallInCallOrigAdapter
+ */
+ @Override
+ protected void createSuperCallInCallOrig(int joinpointId) {
+ assert (isTransformationActive) : "No transformation active";
+ nodes.add(new CreateSuperCallInCallOrigAdapter(
+ getInternalSuperClassName(), joinpointId));
+
+ }
+
+ /**
+ * Creates a call of callAllBindings in the original method.
+ * @see CreateCallAllBindingsCallInOrgMethod
+ */
+ @Override
+ protected void createCallAllBindingsCallInOrgMethod(Method boundMethod,
+ int boundMethodId, boolean needToAddMethod) {
+ assert (isTransformationActive) : "No transformation active";
+ if (needToAddMethod) {
+ String desc = boundMethod.getSignature();
+ Type[] args = Type.getArgumentTypes(desc);
+ multiAdapter.addVisitor(new AddEmptyMethodAdapter(writer, boundMethod.getName(),
+ boundMethod.getAccessFlags(), desc, null, boundMethod.getSignature(), args.length+1/*maxLocals*/));
+ }
+ nodes.add(new CreateCallAllBindingsCallInOrgMethod(boundMethod,
+ boundMethodId));
+
+ }
+
+ /**
+ * Prepares a the class for a decapsulation of one of its methods
+ */
+ @Override
+ protected void weaveMethodAccess(Method method, int accessId) {
+ nodes.add(new CreateMethodAccessAdapter(method, accessId));
+
+ }
+
+ /**
+ * Prepares a the class for a decapsulation of one of its fields
+ */
+ @Override
+ protected void weaveFieldAccess(Field field, int accessId) {
+ nodes.add(new CreateFieldAccessAdapter(field, accessId));
+
+ }
+
+ /**
+ * Write the bytecode in the directory ./otdyn to the hard disk,
+ * if the system property "ot.dump" is set.
+ */
+ private void dump() {
+ if (!dumping)
+ return;
+
+ FileOutputStream fos = null;
+ try {
+ String name = getName();
+ int index = name.indexOf('/');
+ if (index == -1)
+ index = name.indexOf('.');
+ File dir = new File("otdyn");
+ if (!dir.exists())
+ dir.mkdir();
+ String filename = "otdyn/" + name.substring(index + 1) + ".class";
+ fos = new FileOutputStream(filename);
+ fos.write(allocateAndGetBytecode());
+ fos.close();
+ } catch (Exception e) {
+ // TODO (ofra): Log error while dumping
+ e.printStackTrace();
+ }
+ }
+
+ private int n = 0; // counts dump files for this class
+ public void dump(byte[] bytecode, String postfix) {
+ if (!dumping)
+ return;
+
+ FileOutputStream fos = null;
+ try {
+ String name = getName();
+ int index = name.indexOf('/');
+ if (index == -1)
+ index = name.indexOf('.');
+ File dir = new File("otdyn");
+ if (!dir.exists())
+ dir.mkdir();
+ String filename = "otdyn/" + name.substring(index + 1) + postfix+".#"+(n++);
+ fos = new FileOutputStream(filename);
+ fos.write(bytecode);
+ fos.close();
+ } catch (Exception e) {
+ // TODO (ofra): Log error while dumping
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Redefines the class
+ */
+ private void redefine() {
+ try {
+ Class<?> clazz = this.loader.loadClass(this.getName()); // boot classes may have null classloader, can't be redefined anyway?
+ byte[] bytecode = allocateAndGetBytecode();
+ dump(bytecode, "redef");
+ RedefineStrategyFactory.getRedefineStrategy().redefine(clazz, bytecode);
+ } catch (Throwable t) {
+ throw new RuntimeException("Error occured while dynamically redefining class " + getName()+"\n"+t.getMessage(), t);
+ }
+ }
+
+ /**
+ * Do all transformations needed at load time
+ */
+ @Override
+ protected void prepareAsPossibleBaseClass() {
+ if (!isFirstTransformation)
+ return;
+
+ addInterface(ClassNames.I_BOUND_BASE_SLASH);
+
+ int methodModifiers = Types.ACCESS_PUBLIC;
+ if (isInterface())
+ methodModifiers |= Opcodes.ACC_ABSTRACT;
+
+ if (!isInterface())
+ addField(ConstantMembers.roleSet, Types.ACCESS_PUBLIC);
+
+ addEmptyMethod(ConstantMembers.callOrig, methodModifiers, null, null);
+ addEmptyMethod(ConstantMembers.callAllBindingsClient, methodModifiers, null, null);
+
+ // the methods callOrigStatic and accessStatic have to already exist to call it in a concrete team
+ if (!isInterface()) {
+ addEmptyMethod(getCallOrigStatic(), Types.ACCESS_PUBLIC + Types.ACCESS_STATIC, null, null);
+ addEmptyMethod(ConstantMembers.accessStatic, Types.ACCESS_PUBLIC + Types.ACCESS_STATIC, null, null);
+ }
+
+ addEmptyMethod(ConstantMembers.access, methodModifiers, null, null);
+ addEmptyMethod(ConstantMembers.addOrRemoveRole, methodModifiers, null, null);
+
+ if (!isInterface())
+ multiAdapter.addVisitor(new AddAfterClassLoadingHook(this.writer, this));
+ }
+
+ /** Get the suitable variant of _OT$callOrigStatic, respecting synth args for static role methods. */
+ Method getCallOrigStatic() {
+ if (isRole())
+ return ConstantMembers.callOrigStaticRoleVersion(getEnclosingClassName());
+ else
+ return ConstantMembers.callOrigStatic;
+ }
+
+ /**
+ * Prepares the class for implicit team activation by adding appropriate calls to
+ * _OT$implicitlyActivate(), _OT$implicitlyDeactivate() into all relevant methods.
+ */
+ @Override
+ protected void prepareTeamActivation() {
+ if (!isFirstTransformation || isInterface())
+ return;
+ if (isTeam() || isRole())
+ multiAdapter.addVisitor(new AddImplicitActivationAdapter(this.writer, this));
+ AddGlobalTeamActivationAdapter.checkAddVisitor(this.multiAdapter, this.writer);
+ }
+
+ /**
+ * Prepares the methods callAllBindings and callOrig with an empty
+ * switch statement
+ */
+ @Override
+ protected void prepareForFirstTransformation() {
+ if (!isTransformed && !isInterface()) {
+ nodes.add(new CreateSwitchAdapter(ConstantMembers.callOrig));
+ nodes.add(new CreateSwitchForCallAllBindingsNode());
+ nodes.add(new CreateAddRemoveRoleMethod());
+ isTransformed = true;
+ }
+ }
+
+ /**
+ * Prepares the method callOrigStatic with an empty
+ * switch statement
+ */
+ @Override
+ protected void prepareForFirstStaticTransformation() {
+ if (!isTransformedStatic && !isInterface()) {
+ nodes.add(new CreateSwitchAdapter(getCallOrigStatic(), isRole()));
+ isTransformedStatic = true;
+ }
+ }
+
+ /**
+ * Prepares the methods access and accessStatic with an empty
+ * switch statement
+ */
+ @Override
+ protected void prepareForFirstMemberAccess() {
+ if (!isTransformedForMemberAccess && !isInterface()) {
+ nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.access,
+ getInternalSuperClassName(), this));
+ nodes.add(new CreateSwitchForAccessAdapter(ConstantMembers.accessStatic,
+ getInternalSuperClassName(), this));
+ isTransformedForMemberAccess = true;
+ }
+
+ }
+
+ /**
+ * Was the class already transformed?
+ */
+ @Override
+ protected boolean isFirstTransformation() {
+ return isFirstTransformation;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/Attributes.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/Attributes.java new file mode 100644 index 000000000..4c56ef3c7 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/Attributes.java @@ -0,0 +1,340 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
+import org.eclipse.objectteams.otredyn.bytecode.Binding;
+import org.eclipse.objectteams.otredyn.bytecode.ClassRepository;
+import org.eclipse.objectteams.otredyn.runtime.ClassIdentifierProviderFactory;
+import org.eclipse.objectteams.otredyn.runtime.IBinding;
+import org.eclipse.objectteams.otredyn.runtime.IClassIdentifierProvider;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+
+/**
+ * This class contains all classes representing OT/J class file attributes
+ * @author Oliver Frank
+ */
+abstract class Attributes {
+ protected final static String ATTRIBUTE_OT_DYN_CALLIN_BINDINGS="OTDynCallinBindings";
+ protected final static String ATTRIBUTE_CALLIN_PRECEDENCE = "CallinPrecedence";
+ protected final static String ATTRIBUTE_OT_CLASS_FLAGS = "OTClassFlags";
+ protected final static String ATTRIBUTE_OT_SPECIAL_ACCESS = "OTSpecialAccess";
+
+ protected final static Attribute[] attributes = {
+ new CallinBindingsAttribute(0),
+ new CallinPrecedenceAttribute(0),
+ new OTClassFlagsAttribute(0),
+ new OTSpecialAccessAttribute()
+ };
+
+ protected static class CallinBindingsAttribute extends Attribute {
+ static final short COVARIANT_BASE_RETURN = 8;
+
+ /** Represents all base method bindings of one callin binding. */
+ protected static class MultiBinding {
+ private String roleClassName;
+ private String callinLabel;
+ private String baseClassName;
+ private String[] baseMethodNames;
+ private String[] baseMethodSignatures;
+ private String[] declaringBaseClassNames;
+ private int callinModifier;
+ private int[] callinIds;
+ private boolean isHandleCovariantReturn;
+ MultiBinding(String roleName, String callinLabel,
+ String baseClassName,
+ String[] baseMethodNames, String[] baseMethodSignatures, String[] declaringBaseClassNames,
+ int callinModifier, int[] callinIds, boolean handleCovariantReturn)
+ {
+ this.roleClassName = roleName;
+ this.callinLabel = callinLabel;
+ this.baseClassName = baseClassName;
+ this.baseMethodNames = baseMethodNames;
+ this.baseMethodSignatures = baseMethodSignatures;
+ this.declaringBaseClassNames = declaringBaseClassNames;
+ this.callinModifier = callinModifier;
+ this.callinIds = callinIds;
+ this.isHandleCovariantReturn = handleCovariantReturn;
+ }
+ protected String getRoleClassName() {
+ return roleClassName;
+ }
+
+ protected String getBaseClassName() {
+ return baseClassName;
+ }
+
+ protected String[] getBaseMethodNames() {
+ return baseMethodNames;
+ }
+
+ protected String[] getBaseMethodSignatures() {
+ return baseMethodSignatures;
+ }
+
+ protected int getCallinModifier() {
+ return this.callinModifier;
+ }
+
+ protected int[] getCallinIds() {
+ return callinIds;
+ }
+
+ protected String getCallinLabel() {
+ return callinLabel;
+ }
+ public boolean isHandleCovariantReturn() {
+ return this. isHandleCovariantReturn;
+ }
+ public String[] getDeclaringBaseClassName() {
+ return this.declaringBaseClassNames;
+ }
+ }
+
+ private MultiBinding[] bindings;
+
+ public CallinBindingsAttribute(int bindingsCount) {
+ super(ATTRIBUTE_OT_DYN_CALLIN_BINDINGS);
+ this.bindings = new MultiBinding[bindingsCount];
+ }
+
+ private void addBinding(int i, String roleName, String callinLabel,
+ String baseClassName,
+ String[] baseMethodNames, String[] baseMethodSignatures, String[] declaringBaseClassNames,
+ String callinModifierName, int[] callinIds, boolean handleCovariantReturn) {
+ int callinModifier = 0;
+ if ("before".equals(callinModifierName))
+ callinModifier = Binding.BEFORE;
+ else if ("after".equals(callinModifierName))
+ callinModifier = Binding.AFTER;
+ else
+ callinModifier = Binding.REPLACE;
+ this.bindings[i] = new MultiBinding(roleName, callinLabel,
+ baseClassName,
+ baseMethodNames, baseMethodSignatures, declaringBaseClassNames,
+ callinModifier, callinIds, handleCovariantReturn);
+ }
+
+ @Override
+ protected Attribute read(ClassReader cr, int off, int len,
+ char[] buf, int codeOff, Label[] labels)
+ {
+ int bindingsCount = cr.readShort(off); off += 2;
+ CallinBindingsAttribute attr = new CallinBindingsAttribute(bindingsCount);
+ for (int i = 0; i < bindingsCount; i++) {
+ String roleName = cr.readUTF8(off, buf); off += 2;
+ String callinLabel = cr.readUTF8(off, buf); off += 2;
+ /* skip roleSelector, roleSignature */ off += 4;
+ String callinModifier = cr.readUTF8(off, buf); off += 2;
+ int flags = cr.readByte(off); off += 1;
+ String baseClassName = cr.readUTF8(off, buf); off += 2;
+ /* skip filename & lineNumber & lineOffset */ off += 6;
+ int baseMethodsCount = cr.readShort(off); off += 2;
+ String[] baseMethodNames = new String[baseMethodsCount];
+ String[] baseMethodSignatures = new String[baseMethodsCount];
+ String[] declaringBaseClassNames = new String[baseMethodsCount];
+ int[] callinIds = new int[baseMethodsCount];
+ for (int m = 0; m < baseMethodsCount; m++) {
+ String baseMethodName = cr.readUTF8(off, buf); off += 2;
+ String baseMethodSignature = cr.readUTF8(off, buf); off += 2;
+ String declaringBaseClass = cr.readUTF8(off, buf); off += 2;
+ int callinId = cr.readInt(off); off += 4;
+ baseMethodNames[m] = baseMethodName;
+ baseMethodSignatures[m] = baseMethodSignature;
+ declaringBaseClassNames[m] = declaringBaseClass;
+ callinIds[m] = callinId;
+ /* skip baseFlags & translationFlags */ off += 3;
+ }
+ attr.addBinding(i, roleName, callinLabel,
+ baseClassName,
+ baseMethodNames, baseMethodSignatures, declaringBaseClassNames,
+ callinModifier, callinIds, ((flags & COVARIANT_BASE_RETURN) != 0));
+ }
+ return attr;
+ }
+
+ public MultiBinding[] getBindings() {
+ return this.bindings;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ for (MultiBinding binding : this.bindings) {
+ buf.append(binding.getBaseClassName());
+ int[] callinIds = binding.getCallinIds();
+ String[] baseMethodNames = binding.getBaseMethodNames();
+ String[] baseMethodSignatures = binding.getBaseMethodSignatures();
+ for (int i=0; i<callinIds.length; i++) {
+ buf.append("\n\t{");
+ buf.append(callinIds[i]);
+ buf.append("} ");
+ buf.append(baseMethodNames[i]);
+ buf.append(baseMethodSignatures[i]);
+ }
+ }
+ return buf.toString();
+ }
+ }
+ protected static class CallinPrecedenceAttribute extends Attribute {
+ String[] labels;
+ public CallinPrecedenceAttribute(int elementCount) {
+ super(ATTRIBUTE_CALLIN_PRECEDENCE);
+ this.labels = new String[elementCount];
+ }
+ @Override
+ protected Attribute read(ClassReader cr, int off, int len,
+ char[] buf, int codeOff, Label[] labels)
+ {
+ int elementCount = cr.readShort(off); off += 2;
+ CallinPrecedenceAttribute attr = new CallinPrecedenceAttribute(elementCount);
+ for (int i = 0; i < elementCount; i++) {
+ attr.labels[i] = cr.readUTF8(off, buf); off += 2;
+ }
+ return attr;
+ }
+ }
+ protected static class OTClassFlagsAttribute extends Attribute {
+ int flags;
+ protected OTClassFlagsAttribute(int flags) {
+ super(ATTRIBUTE_OT_CLASS_FLAGS);
+ this.flags = flags;
+ }
+ @Override
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels)
+ {
+ return new OTClassFlagsAttribute(cr.readUnsignedShort(off));
+ }
+ }
+ protected static class OTSpecialAccessAttribute extends Attribute {
+ class DecapsField {
+ String accessMode;
+ boolean isStatic;
+ String baseclass, name, desc;
+ public int accessId;
+ public DecapsField(String baseclass, String name, String desc, int accessId, String accessMode, boolean isStatic) {
+ this.baseclass = baseclass;
+ this.name = name;
+ this.desc = desc;
+ this.accessId = accessId;
+ this.accessMode = accessMode;
+ this.isStatic = isStatic;
+ }
+ }
+ class DecapsMethod {
+ String baseclass, name, desc;
+ int id;
+ boolean isStatic;
+ DecapsMethod(String baseclass, String name, String desc, int id, boolean isStatic) {
+ this.baseclass = baseclass;
+ this.name = name;
+ this.desc = desc;
+ this.id = id;
+ this.isStatic = isStatic;
+ }
+ }
+ private static final int DECAPSULATION_METHOD_ACCESS= 4; // kinds are disjoint from those used by the old OTRE
+ private static final int CALLOUT_FIELD_ACCESS = 5;
+ private static final int SUPER_METHOD_ACCESS = 6;
+
+ List<DecapsMethod> methods = new ArrayList<DecapsMethod>();
+ List<DecapsField> fields = new ArrayList<DecapsField>();
+
+ protected OTSpecialAccessAttribute() {
+ super(ATTRIBUTE_OT_SPECIAL_ACCESS);
+ }
+ @Override
+ protected Attribute read(ClassReader cr, int off, int len, char[] buf,
+ int codeOff, Label[] labels)
+ {
+ OTSpecialAccessAttribute attr = new OTSpecialAccessAttribute();
+ int size = cr.readUnsignedShort(off); off+=2;
+ for (int i=0; i<size; i++) {
+ int kind = cr.readByte(off++);
+ switch (kind) {
+ case DECAPSULATION_METHOD_ACCESS:
+ attr.readMethodAccess(cr, off, buf); off+=6;
+ break;
+ case CALLOUT_FIELD_ACCESS:
+ attr.readFieldAccess(cr, off, buf); off+=8;
+ default:
+ // FIXME(SH): handle!
+ }
+ }
+ return attr;
+ }
+ private void readMethodAccess(ClassReader cr, int off, char[] buf) {
+ String className = cr.readUTF8(off, buf);
+ String encodedName = cr.readUTF8(off+2, buf);
+ String methodDesc = cr.readUTF8(off+4, buf);
+ int id = cr.readUnsignedShort(off+6);
+ boolean isStatic = false;
+ String baseClass;
+ String methodName;
+ int pos = encodedName.indexOf('?');
+ if (pos == -1) {
+ pos = encodedName.indexOf('!');
+ isStatic = true;
+ }
+ baseClass = encodedName.substring(0, pos);
+ methodName = encodedName.substring(pos+1);
+ this.methods.add(new DecapsMethod(baseClass, methodName, methodDesc, id, isStatic));
+ }
+ private void readFieldAccess(ClassReader cr, int off, char[] buf) {
+ int accessId = cr.readByte(off);
+ int flags = cr.readByte(off+1);
+ String className = cr.readUTF8(off+2, buf);
+ String fieldName = cr.readUTF8(off+4, buf);
+ String fieldDesc = cr.readUTF8(off+6, buf);
+ boolean isStatic = (flags & 2) != 0;
+ String accessMode = (flags & 1) == 1 ? "set" : "get";
+ this.fields.add(new DecapsField(className, fieldName, fieldDesc, accessId, accessMode, isStatic));
+ }
+ public void registerAt(AsmBoundClass clazz) {
+ ClassRepository repo = ClassRepository.getInstance();
+ IClassIdentifierProvider provider = ClassIdentifierProviderFactory.getClassIdentifierProvider();
+
+ for (DecapsMethod dMethod : this.methods) {
+ // FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings:
+ // bypassing the identifier provider (we don't have a Class<?> yet):
+ // String boundClassIdentifier = provider.getBoundClassIdentifier(clazz, dMethod.baseclass);
+ AbstractBoundClass baseclass = repo.getBoundClass(dMethod.baseclass, dMethod.baseclass.replace('.', '/'), clazz.getClassLoader());
+ // register the target method:
+ baseclass.getMethod(dMethod.name, dMethod.desc, false/*covariantReturn*/, dMethod.isStatic);
+ clazz.recordAccessId(dMethod.id);
+ clazz.addBinding(new Binding(clazz, dMethod.baseclass, dMethod.name, dMethod.desc, dMethod.id, IBinding.BindingType.METHOD_ACCESS));
+ }
+
+ for (DecapsField dField: this.fields) {
+ // FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings:
+ // bypassing the identifier provider (we don't have a Class<?> yet):
+ // String boundClassIdentifier = provider.getBoundClassIdentifier(clazz, dMethod.baseclass);
+ AbstractBoundClass baseclass = repo.getBoundClass(dField.baseclass, dField.baseclass.replace('.', '/'), clazz.getClassLoader());
+ // register the target field:
+ baseclass.getField(dField.name, dField.desc);
+ clazz.recordAccessId(dField.accessId);
+ clazz.addBinding(new Binding(clazz, dField.baseclass, dField.name, dField.desc, dField.accessId, IBinding.BindingType.FIELD_ACCESS));
+ }
+ }
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateCallAllBindingsCallInOrgMethod.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateCallAllBindingsCallInOrgMethod.java new file mode 100644 index 000000000..bb067c41c --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateCallAllBindingsCallInOrgMethod.java @@ -0,0 +1,146 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+
+/**
+ * This class creates and adds the instructions, that are needed
+ * to call the method callAllBindings to a method.<br/> <br/>
+ * The instructions looks as follows:<br/>
+ * <code>
+ * Object[] args = {args1, ..., argsn};<br/>
+ * this.callAllBindings(boundMethodId, args);
+ * </code>
+ * @author Oliver Frank
+ */
+public class CreateCallAllBindingsCallInOrgMethod extends
+ AbstractTransformableClassNode {
+
+ private Method orgMethod;
+ private int boundMethodId;
+
+ public CreateCallAllBindingsCallInOrgMethod(Method orgMethod,
+ int boundMethodId) {
+ this.orgMethod = orgMethod;
+ this.boundMethodId = boundMethodId;
+ }
+
+ @Override
+ public void transform() {
+ MethodNode method = getMethod(orgMethod);
+ method.instructions.clear();
+
+ // start of try-block:
+ LabelNode start = new LabelNode();
+ method.instructions.add(start);
+
+ // put this on the stack
+ method.instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ // put boundMethodId on the stack
+ method.instructions.add(createLoadIntConstant(boundMethodId));
+ Type[] args = Type.getArgumentTypes(method.desc);
+ // box the arguments
+ method.instructions.add(getBoxingInstructions(args, false));
+
+ // this.callAllBindings(boundMethodId, args);
+ method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
+ this.name, ConstantMembers.callAllBindingsClient.getName(),
+ ConstantMembers.callAllBindingsClient.getSignature()));
+ Type returnType = Type.getReturnType(method.desc);
+ method.instructions
+ .add(getUnboxingInstructionsForReturnValue(returnType));
+
+ // catch and unwrap SneakyException:
+ addCatchSneakyException(method, start);
+
+ int localSlots = 0;
+ int maxArgSize = 1;
+ for (Type type : args) {
+ int size = type.getSize();
+ localSlots += size;
+ if (size == 2)
+ maxArgSize = 2;
+ }
+ method.maxStack = args.length > 0 ? 5+maxArgSize : 3;
+ method.maxLocals = localSlots+1;
+ }
+
+ @SuppressWarnings("unchecked") // access to raw type List in ASM
+ void addCatchSneakyException(MethodNode method, LabelNode start) {
+ method.tryCatchBlocks.add(getCatchBlock(method.instructions, start, orgMethod));
+ }
+
+ TryCatchBlockNode getCatchBlock(InsnList instructions, LabelNode start, Method method) {
+ // end (exclusive) of try-block
+ LabelNode end = new LabelNode();
+ instructions.add(end);
+
+ // catch (SneakyException e) { e.rethrow(); }
+ LabelNode catchSneaky = new LabelNode();
+ instructions.add(catchSneaky);
+ instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, ClassNames.SNEAKY_EXCEPTION_SLASH, ClassNames.RETHROW_SELECTOR, ClassNames.RETHROW_SIGNATURE));
+
+ // never reached, just to please the verifier:
+ Type returnType = Type.getReturnType(method.getSignature());
+ instructions.add(getReturnInsn(returnType));
+ return new TryCatchBlockNode(start, end, catchSneaky, ClassNames.SNEAKY_EXCEPTION_SLASH);
+ }
+
+ protected InsnList getReturnInsn(Type returnType) {
+ InsnList instructions = new InsnList();
+ switch (returnType.getSort()) {
+ case Type.VOID:
+ instructions.add(new InsnNode(Opcodes.RETURN));
+ break;
+ case Type.ARRAY:
+ case Type.OBJECT:
+ instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ break;
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.INT:
+ case Type.SHORT:
+ case Type.LONG:
+ instructions.add(new InsnNode(Opcodes.ICONST_0));
+ instructions.add(new InsnNode(Opcodes.IRETURN));
+ break;
+ case Type.DOUBLE:
+ instructions.add(new InsnNode(Opcodes.DCONST_0));
+ instructions.add(new InsnNode(Opcodes.DRETURN));
+ break;
+ case Type.FLOAT:
+ instructions.add(new InsnNode(Opcodes.FCONST_0));
+ instructions.add(new InsnNode(Opcodes.FRETURN));
+ break;
+ }
+ return instructions;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInCallAllBindingsAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInCallAllBindingsAdapter.java new file mode 100644 index 000000000..9b49160f1 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInCallAllBindingsAdapter.java @@ -0,0 +1,76 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Create the code for the dispatch from a base class to the teams
+ * in the method callAllBindings. <br/> <br/>
+ * The code was generated as follows: <br/>
+ * <code>
+ * switch (boundMethodId) { // this was generated in CreateSwitchAdapter <br/>
+ * ... <br/>
+ * case (boundMethodId): <br/>
+ * Teams[] teams = TeamManager.getTeams(joinpointId) <br/>
+ * if (teams == null) { <br/>
+ * break; <br/>
+ * } <br/>
+ * <br/>
+ * Team t = teams[0]; <br/>
+ * int[] callinIds = TeamManager.getCallinIds(joinpointId); <br/>
+ * Object[] args = {arg1, ... , argn};
+ * return team._OT$callAllBindings(this, teams, 0, callinIds, boundMethodId, args);
+ * } <br/>
+ * </code>
+ * @author Oliver Frank
+ */
+public class CreateDispatchCodeInCallAllBindingsAdapter extends
+ AbstractCreateDispatchCodeAdapter {
+
+ private int joinpointId;
+ private int boundMethodId;
+
+ public CreateDispatchCodeInCallAllBindingsAdapter(int joinpointId,
+ int boundMethodId) {
+ super(false);
+ this.joinpointId = joinpointId;
+ this.boundMethodId = boundMethodId;
+ }
+
+ @Override
+ public void transform() {
+ MethodNode callAllBindings = getMethod(ConstantMembers.callAllBindingsClient);
+ InsnList instructions = getDispatchCode(callAllBindings, joinpointId, boundMethodId);
+ addNewLabelToSwitch(callAllBindings.instructions, instructions, boundMethodId);
+ callAllBindings.maxStack = getMaxStack();
+ callAllBindings.maxLocals = getMaxLocals();
+ }
+
+ @Override
+ protected InsnList getBoxedArguments(Type[] args) {
+ InsnList instructions = new InsnList();
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 2));
+ return instructions;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInOrgMethodAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInOrgMethodAdapter.java new file mode 100644 index 000000000..27e8cbe2e --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateDispatchCodeInOrgMethodAdapter.java @@ -0,0 +1,100 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Create the code for the dispatch from a base class to the teams
+ * in the original method. <br/> <br/>
+ * The code was generated as follows: <br/>
+ * <code>
+ * Teams[] teams = TeamManager.getTeams(joinpointId) <br/>
+ * if (teams == null) { <br/>
+ * Object args[] = {arg1, ..., argn}; <br/>
+ * return callOrig(boundMethodId, args); <br/>
+ * } <br/>
+ * <br/>
+ * Team t = teams[0]; <br/>
+ * int[] callinIds = TeamManager.getCallinIds(joinpointId); <br/>
+ * Object[] args = {arg1, ... , argn};
+ * return team._OT$callAllBindings(this, teams, 0, callinIds, boundMethodId, args);
+ * } <br/>
+ * </code>
+ * @author Oliver Frank
+ */
+public class CreateDispatchCodeInOrgMethodAdapter extends
+ AbstractCreateDispatchCodeAdapter {
+ private Method method;
+ private int joinPointId;
+ private int boundMethodId;
+
+ public CreateDispatchCodeInOrgMethodAdapter(Method method, int joinPointId, int boundMethodId) {
+ super(true);
+ this.method = method;
+ this.joinPointId = joinPointId;
+ this.boundMethodId = boundMethodId;
+ }
+
+ @Override
+ public void transform() {
+ MethodNode orgMethod = getMethod(method);
+
+ orgMethod.instructions.clear();
+ orgMethod.instructions.add(getDispatchCode(orgMethod, joinPointId, boundMethodId));
+ orgMethod.maxStack = getMaxStack();
+ orgMethod.maxLocals = getMaxLocals();
+ }
+
+ @Override
+ protected InsnList getBoxedArguments(Type[] args) {
+ return getBoxingInstructions(args, true);
+ }
+
+ @Override
+ protected InsnList createInstructionsToCheackTeams(MethodNode method) {
+ InsnList instructions = new InsnList();
+ instructions.add(new InsnNode(Opcodes.DUP));
+ LabelNode label = new LabelNode();
+ //if (teams == null) {
+ instructions.add(new JumpInsnNode(Opcodes.IFNONNULL, label));
+ instructions.add(new InsnNode(Opcodes.POP));
+ //put the boundMethodId on the stack
+ instructions.add(createLoadIntConstant(boundMethodId));
+ Type[] args = Type.getArgumentTypes(method.desc);
+ // box the arguments
+ instructions.add(getBoxingInstructions(args, (method.access & Opcodes.ACC_STATIC) != 0));
+ //callOrigStatic(boundMethodId, args);
+ instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, name, ConstantMembers.callOrigStatic.getName(), ConstantMembers.callOrigStatic.getSignature()));
+ Type returnType = Type.getReturnType(method.desc);
+ instructions
+ .add(getUnboxingInstructionsForReturnValue(returnType));
+ instructions.add(label);
+
+ return instructions;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateFieldAccessAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateFieldAccessAdapter.java new file mode 100644 index 000000000..2b240e36f --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateFieldAccessAdapter.java @@ -0,0 +1,130 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Field;
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * Creates and adds the instructions,
+ * that are needed to access a not visible field from the team
+ * in the method access or accessStatic as follows:<br/> <br/>
+ * case (memberId) { // generated by CreateSwitchForAccessAdapter <br/>
+ * if (opKind == 0) { <br/>
+ * return field; <br/>
+ * } else { <br/>
+ * field = args[0]; <br/>
+ * } <br/>
+ * break; <br/>
+ * }
+ *
+ * @author Oliver Frank
+ */
+public class CreateFieldAccessAdapter extends AbstractTransformableClassNode {
+
+ private Field field;
+ private int accessId;
+ private Method access;
+ private int firstArgIndex;
+
+ public CreateFieldAccessAdapter(Field field, int accessId) {
+ this.field = field;
+ this.accessId = accessId;
+ if (field.isStatic()) {
+ access = ConstantMembers.accessStatic;
+ firstArgIndex = 0;
+ } else {
+ access = ConstantMembers.access;
+ firstArgIndex = 1;
+ }
+ }
+
+ @Override
+ public void transform() {
+
+ InsnList instructions = new InsnList();
+ // put accessId on the stack
+ instructions.add(new IntInsnNode(Opcodes.ILOAD, firstArgIndex + 1));
+ // read or write access
+ LabelNode writeAccess = new LabelNode();
+ instructions.add(new JumpInsnNode(Opcodes.IFNE, writeAccess));
+ // read access
+ if (field.isStatic()) {
+ // get value of field
+ instructions.add(new FieldInsnNode(Opcodes.GETSTATIC, name, field
+ .getName(), field.getSignature()));
+ } else {
+ // put "this" on the stack
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ // get value of field
+ instructions.add(new FieldInsnNode(Opcodes.GETFIELD, name, field
+ .getName(), field.getSignature()));
+ }
+
+ //box value as "Object"
+ Type type = Type.getType(field.getSignature());
+ instructions.add(AsmTypeHelper.getBoxingInstructionForType(type));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+
+ //write access
+ instructions.add(writeAccess);
+ //put "args" on the stack
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + 2));
+ //get the first element of "args"
+ instructions.add(new InsnNode(Opcodes.ICONST_0));
+ instructions.add(new InsnNode(Opcodes.AALOAD));
+ //unbox it
+ if (type.getSort() != Type.ARRAY && type.getSort() != Type.OBJECT) {
+ String objectType = AsmTypeHelper.getObjectType(type);
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
+ instructions.add(AsmTypeHelper.getUnboxingInstructionForType(type, objectType));
+ } else {
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, type.getInternalName()));
+ }
+
+ if (field.isStatic()) {
+ //save value in field
+ instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, name, field.getName(), field.getSignature()));
+ } else {
+ //put "this" on the stack
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ instructions.add(new InsnNode(Opcodes.SWAP));
+ //save value in field
+ instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, name, field.getName(), field.getSignature()));
+ }
+
+ //dummy return
+ instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+
+ //add the instructions to a new label in the existing switch
+ MethodNode method = getMethod(access);
+ addNewLabelToSwitch(method.instructions, instructions, accessId);
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateMethodAccessAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateMethodAccessAdapter.java new file mode 100644 index 000000000..acd100532 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateMethodAccessAdapter.java @@ -0,0 +1,121 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * Creates and adds the instructions,
+ * that are needed to access a not visible method from the team
+ * in the method access or accessStatic as follows:<br/> <br/>
+ * case (memberId) { // generated by CreateSwitchForAccessAdapter <br/>
+ * return orgMethod(args[0], ..., args[args.length]); <br/>
+ * }
+ *
+ * @author Oliver Frank
+ */
+public class CreateMethodAccessAdapter extends AbstractTransformableClassNode {
+ private Method method;
+ private int accessId;
+ private Method access;
+ private int firstArgIndex;
+
+ public CreateMethodAccessAdapter(Method method, int accessId) {
+ this.method = method;
+ this.accessId = accessId;
+ if (method.isStatic()) {
+ access = ConstantMembers.accessStatic;
+ firstArgIndex = 0;
+ } else {
+ access = ConstantMembers.access;
+ firstArgIndex = 1;
+ }
+ }
+
+ @Override
+ public void transform() {
+ MethodNode methodNode = getMethod(method);
+ InsnList instructions = new InsnList();
+
+ //put "this" on the stack for a non-static method
+ if (!method.isStatic()) {
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+ }
+
+ //Unbox arguments
+ Type[] args = Type.getArgumentTypes(methodNode.desc);
+
+ if (args.length > 0) {
+ instructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + 2));
+
+
+ for (int i = 0; i < args.length; i++) {
+ if (i < args.length - 1) {
+ instructions.add(new InsnNode(Opcodes.DUP));
+ }
+ instructions.add(createLoadIntConstant(i));
+ instructions.add(new InsnNode(Opcodes.AALOAD));
+ Type arg = args[i];
+ if (arg.getSort() != Type.ARRAY && arg.getSort() != Type.OBJECT) {
+ String objectType = AsmTypeHelper.getObjectType(arg);
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
+ instructions.add(AsmTypeHelper.getUnboxingInstructionForType(arg, objectType));
+ } else {
+ instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, arg.getInternalName()));
+ }
+ }
+ }
+
+ //call original method
+ int opcode = Opcodes.INVOKEVIRTUAL;
+ if (method.isStatic()) {
+ opcode = Opcodes.INVOKESTATIC;
+ }
+ instructions.add(new MethodInsnNode(opcode, name, method.getName(), method.getSignature()));
+
+
+ //box return value
+ Type returnType = Type.getReturnType(methodNode.desc);
+
+ if (returnType.getSort() != Type.OBJECT &&
+ returnType.getSort() != Type.ARRAY &&
+ returnType.getSort() != Type.VOID) {
+
+ instructions.add(AsmTypeHelper.getBoxingInstructionForType(returnType));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ } else if (returnType.getSort() == Type.VOID) {
+ instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ } else {
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+
+ //add the instructions to a new label in the existing switch
+ MethodNode access = getMethod(this.access);
+ addNewLabelToSwitch(access.instructions, instructions, accessId);
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallInCallOrigAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallInCallOrigAdapter.java new file mode 100644 index 000000000..e81bf8230 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSuperCallInCallOrigAdapter.java @@ -0,0 +1,58 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * This class creates the instructions, that are needed
+ * to call super.callOrig(boundMethodId, args)
+ * @author Oliver Frank
+ */
+public class CreateSuperCallInCallOrigAdapter extends AbstractTransformableClassNode {
+
+ private int joinpointId;
+ private String superClassName;
+
+ public CreateSuperCallInCallOrigAdapter(String superClassName,
+ int joinpointId) {
+ this.superClassName = superClassName;
+ this.joinpointId = joinpointId;
+ }
+
+ @Override
+ public void transform() {
+ MethodNode callOrig = getMethod(ConstantMembers.callOrig);
+ InsnList instructions = new InsnList();
+
+ Type[] args = Type.getArgumentTypes(callOrig.desc);
+ addInstructionsForLoadArguments(instructions, args, false);
+
+ instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
+ superClassName, ConstantMembers.callOrig.getName(),
+ ConstantMembers.callOrig.getSignature()));
+ instructions.add(new InsnNode(Opcodes.ARETURN));
+ addNewLabelToSwitch(callOrig.instructions, instructions, joinpointId);
+ callOrig.maxStack = Math.max(callOrig.maxStack, args.length + 1);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchAdapter.java new file mode 100644 index 000000000..dbbabcfd9 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchAdapter.java @@ -0,0 +1,112 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * This class creates an empty switch statement in a method.
+ * @author Oliver Frank
+ */
+public class CreateSwitchAdapter extends AbstractTransformableClassNode {
+ private Method method;
+
+ private int firstArgIndex;
+
+ public CreateSwitchAdapter(Method method) {
+ this.method = method;
+ if (method.isStatic()) {
+ firstArgIndex = 0;
+ } else {
+ firstArgIndex = 1;
+ }
+ }
+
+ public CreateSwitchAdapter(Method method, boolean isRoleMethod) {
+ this.method = method;
+ if (method.isStatic()) {
+ if (isRoleMethod)
+ firstArgIndex = 2;
+ else
+ firstArgIndex = 0;
+ } else {
+ firstArgIndex = 1;
+ }
+ }
+
+ @Override
+ public void transform() {
+ MethodNode methodNode = getMethod(method);
+ if (methodNode == null) return; // doesn't exist, don't transform
+ methodNode.instructions.clear();
+
+ addPreSwitchInstructions(methodNode);
+
+ LabelNode def = new LabelNode();
+ LookupSwitchInsnNode switchNode = new LookupSwitchInsnNode(def, new int[0], new LabelNode[0]);
+
+ methodNode.instructions.add(switchNode);
+ methodNode.instructions.add(def);
+ addInstructionForDefaultLabel(methodNode);
+
+ addPostSwitchInstructions(methodNode);
+ methodNode.maxStack = getMaxStack();
+ }
+
+ /**
+ * Adds instructions after the switch statement
+ * @param method
+ */
+ protected void addPostSwitchInstructions(MethodNode method) {
+ }
+
+ /**
+ * Adds instructions before the switch statement.
+ * @param method
+ */
+ protected void addPreSwitchInstructions(MethodNode method) {
+ method.instructions.add(new IntInsnNode(Opcodes.ILOAD, firstArgIndex));
+ }
+
+ /**
+ * Adds instructions for the default label.
+ * @param method
+ */
+ protected void addInstructionForDefaultLabel(MethodNode method) {
+ method.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
+ method.instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+
+ protected int getMaxStack() {
+ return 1;
+ }
+
+ protected int getFirstArgIndex() {
+ return firstArgIndex;
+ }
+
+ protected Method getMethod() {
+ return method;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java new file mode 100644 index 000000000..adebbf265 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForAccessAdapter.java @@ -0,0 +1,100 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * Initially prepares the method access or accessStatic as follows: <br/><br/>
+ * <code>
+ * int memberId = TeamManager.getMemberId(accessId, caller);<br/>
+ * switch (memberId) {<br/>
+ * default:<br/>
+ * return super.access(accessId, opKind, args, caller);<br/>
+ * }<br/>
+ * </code>
+ * @author Oliver Frank
+ */
+public class CreateSwitchForAccessAdapter extends CreateSwitchAdapter {
+
+ private String superClassName;
+ private AbstractBoundClass clazz;
+ public CreateSwitchForAccessAdapter(Method method, String superClassName, AbstractBoundClass clazz) {
+ super(method);
+ this.superClassName = superClassName;
+ this.clazz = clazz;
+ }
+
+ @Override
+ protected void addPreSwitchInstructions(MethodNode method) {
+ // put "accessId" on the stack
+ method.instructions.add(new IntInsnNode(Opcodes.ILOAD,
+ getFirstArgIndex()));
+ // put "caller".getClass() on the stack
+ method.instructions.add(new IntInsnNode(Opcodes.ALOAD,
+ getFirstArgIndex() + 3));
+ method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"));
+ // call "getMemberId(accessId, callerClass)
+ method.instructions
+ .add(new MethodInsnNode(Opcodes.INVOKESTATIC,
+ ClassNames.TEAM_MANAGER_SLASH,
+ ConstantMembers.getMemberId.getName(),
+ ConstantMembers.getMemberId.getSignature()));
+ }
+
+ @Override
+ protected void addInstructionForDefaultLabel(MethodNode method) {
+ if (superClassName.equals("java/lang/Object")) {
+ method.instructions.add(new TypeInsnNode(Opcodes.NEW, "org/objectteams/NoSuchMethodError"));
+ method.instructions.add(new InsnNode(Opcodes.DUP));
+ method.instructions.add(new IntInsnNode(Opcodes.ILOAD, getFirstArgIndex())); // accessId
+ method.instructions.add(new LdcInsnNode(clazz.getName())); // current class
+ method.instructions.add(new LdcInsnNode("decapsulating access")); // access reason
+ method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "org/objectteams/NoSuchMethodError", "<init>", "(ILjava/lang/String;Ljava/lang/String;)V"));
+ method.instructions.add(new InsnNode(Opcodes.ATHROW));
+ } else {
+ Type[] args = Type.getArgumentTypes(method.desc);
+ addInstructionsForLoadArguments(method.instructions, args, getMethod().isStatic());
+
+ int opcode = Opcodes.INVOKESPECIAL;
+ if (getMethod().isStatic()) {
+ opcode = Opcodes.INVOKESTATIC;
+ }
+ method.instructions.add(new MethodInsnNode(opcode,
+ superClassName, getMethod().getName(),
+ getMethod().getSignature()));
+ method.instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+ }
+
+ @Override
+ protected int getMaxStack() {
+ return 5;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForCallAllBindingsNode.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForCallAllBindingsNode.java new file mode 100644 index 000000000..f8a850a7a --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/CreateSwitchForCallAllBindingsNode.java @@ -0,0 +1,77 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Initially prepares the method callAllBindings as follows: <br/><br/>
+ * <code>
+ * switch (boundMethodId) {<br/>
+ * default:<br/>
+ * break;<br/>
+ * }<br/>
+ * <br/>
+ * return callOrig(boundMethodId, args);<br/>
+ * </code>
+ * @author Oliver Frank
+ */
+public class CreateSwitchForCallAllBindingsNode extends CreateSwitchAdapter {
+ private Type[] args;
+ private LabelNode gotoLabel;
+
+ public CreateSwitchForCallAllBindingsNode() {
+ super(ConstantMembers.callAllBindingsClient);
+ }
+
+ @Override
+ protected void addInstructionForDefaultLabel(MethodNode method) {
+ gotoLabel = new LabelNode();
+ method.instructions.add(new JumpInsnNode(Opcodes.GOTO, gotoLabel));
+ }
+
+ @Override
+ protected void addPostSwitchInstructions(MethodNode method) {
+ method.instructions.add(gotoLabel);
+ method.instructions.add(new IntInsnNode(Opcodes.ALOAD, 0));
+
+ args = Type.getArgumentTypes(method.desc);
+ int length = args.length;
+ for (int i = 0; i < length; i++) {
+ Type arg = args[i];
+ method.instructions.add(new IntInsnNode(arg.getOpcode(Opcodes.ILOAD), i + 1));
+ }
+
+ // return callOrig(boundMethodId, args);
+ method.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, name, ConstantMembers.callOrig.getName(), ConstantMembers.callOrig.getSignature()));
+ method.instructions.add(new InsnNode(Opcodes.ARETURN));
+ }
+
+ @Override
+ protected int getMaxStack() {
+ return args.length + 1;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java new file mode 100644 index 000000000..8438da843 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MoveCodeToCallOrigAdapter.java @@ -0,0 +1,109 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.transformer.names.ConstantMembers;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.InsnList;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+
+
+/**
+ * This class moves the code of a method to callOrig.
+ * @author Oliver Frank
+ */
+public class MoveCodeToCallOrigAdapter extends AbstractTransformableClassNode {
+ private Method method;
+ private int boundMethodId;
+ private int firstArgIndex; // slot index of the first argument (0 (static) or 1 (non-static))
+ private int argOffset; // used to skip synth args if the callOrig method itself is a statid role method
+ private Method callOrig;
+
+ public MoveCodeToCallOrigAdapter(AsmWritableBoundClass clazz, Method method, int boundMethodId) {
+ this.method = method;
+ this.boundMethodId = boundMethodId;
+ if (method.isStatic()) {
+ firstArgIndex = 0;
+ argOffset = clazz.isRole() ? 2 : 0;
+ callOrig = clazz.getCallOrigStatic();
+ } else {
+ firstArgIndex = 1;
+ callOrig = ConstantMembers.callOrig;
+ }
+ }
+
+ public void transform() {
+ MethodNode orgMethod = getMethod(method);
+ MethodNode callOrig = getMethod(this.callOrig);
+
+ Type returnType = Type.getReturnType(orgMethod.desc);
+
+
+
+ InsnList newInstructions = new InsnList();
+
+
+ //Unboxing arguments
+ Type[] args = Type.getArgumentTypes(orgMethod.desc);
+
+ if (args.length > 0) {
+ newInstructions.add(new IntInsnNode(Opcodes.ALOAD, firstArgIndex + argOffset + 1));
+
+ int slot = firstArgIndex + argOffset;
+ for (int i = argOffset; i < args.length; i++) {
+ if (i < args.length - 1) {
+ newInstructions.add(new InsnNode(Opcodes.DUP));
+ }
+ newInstructions.add(createLoadIntConstant(i));
+ newInstructions.add(new InsnNode(Opcodes.AALOAD));
+ Type arg = args[i];
+ if (arg.getSort() != Type.ARRAY && arg.getSort() != Type.OBJECT) {
+ String objectType = AsmTypeHelper.getObjectType(arg);
+ newInstructions.add(new TypeInsnNode(Opcodes.CHECKCAST, objectType));
+ newInstructions.add(AsmTypeHelper.getUnboxingInstructionForType(arg, objectType));
+ } else {
+ newInstructions.add(new TypeInsnNode(Opcodes.CHECKCAST, arg.getInternalName()));
+ }
+
+ newInstructions.add(new IntInsnNode(args[i].getOpcode(Opcodes.ISTORE), slot));
+ slot += arg.getSize();
+ }
+ }
+
+ // replace return of the original method with areturn and box the result value if needed
+ replaceReturn(orgMethod.instructions, returnType);
+
+ newInstructions.add(orgMethod.instructions);
+
+ addNewLabelToSwitch(callOrig.instructions, newInstructions, boundMethodId);
+
+ // a minimum stacksize of 3 is neede to box the arguments
+ callOrig.maxStack = Math.max(Math.max(callOrig.maxStack, orgMethod.maxStack), 3);
+
+ // we have to increment the max. stack size, because we have to put NULL on the stack
+ if (returnType.getSort() == Type.VOID) {
+ callOrig.maxStack += 1;
+ }
+ callOrig.maxLocals = Math.max(callOrig.maxLocals, orgMethod.maxLocals);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MultiClassAdapter.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MultiClassAdapter.java new file mode 100644 index 000000000..0b0bcda74 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/bytecode/asm/MultiClassAdapter.java @@ -0,0 +1,78 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.bytecode.asm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * This class is needed to make it possible, that many
+ * ClassAdpters can manipulate the bytecode in one run.
+ * It just delegates all method calls to the concrete Adapters.
+ * @author Oliver Frank
+ */
+class MultiClassAdapter extends ClassAdapter {
+
+ private List<ClassVisitor> visitors;
+ private ClassVisitor toplevelVisitor;
+
+ public MultiClassAdapter(ClassVisitor v) {
+ super(v);
+ this.visitors = new ArrayList<ClassVisitor>();
+ }
+
+ public void addVisitor(ClassVisitor v) {
+ visitors.add(v);
+ }
+
+ /** We only support one visitor to intercept the toplevel class definition. */
+ public void setToplevelVisitor(ClassVisitor v) {
+ this.toplevelVisitor = v;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature,
+ String superName, String[] interfaces) {
+ if (this.toplevelVisitor != null)
+ this.toplevelVisitor.visit(version, access, name, signature, superName, interfaces);
+ else
+ super.visit(version, access, name, signature, superName, interfaces);
+ }
+ @Override
+ public void visitEnd() {
+ for (ClassVisitor visitor : visitors) {
+ visitor.visitEnd();
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ MethodVisitor result = null;
+ for (ClassVisitor visitor : visitors) {
+ result = visitor.visitMethod(access, name, desc, signature, exceptions);
+ if (result != null) {
+ return result;
+ }
+ }
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/ClassIdentifierProviderFactory.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/ClassIdentifierProviderFactory.java new file mode 100644 index 000000000..c9e8cbe19 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/ClassIdentifierProviderFactory.java @@ -0,0 +1,38 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.runtime;
+
+/**
+ * Provides a singleton instance of {@link IClassIdentifierProvider}.
+ * If no instance is set, this class uses the {@link DefaultClassIdentifierProvider}.
+ * @author Oliver Frank
+ */
+public class ClassIdentifierProviderFactory {
+ private static IClassIdentifierProvider instance;
+
+ public static void setClassIdentifierProvider(IClassIdentifierProvider provider) {
+ instance = provider;
+ }
+
+ public static IClassIdentifierProvider getClassIdentifierProvider() {
+ if (instance == null) {
+ instance = new DefaultClassIdentifierProvider();
+ }
+
+ return instance;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/DefaultClassIdentifierProvider.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/DefaultClassIdentifierProvider.java new file mode 100644 index 000000000..91b10a4bd --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/DefaultClassIdentifierProvider.java @@ -0,0 +1,38 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.runtime;
+
+/**
+ * Default implementation of {@link IClassIdentifierProvider}, that just
+ * returns the class name as identifier. This implementation could be used
+ * in a standard java context.
+ * @author Oliver Frank
+ */
+public class DefaultClassIdentifierProvider implements IClassIdentifierProvider {
+
+ public String getBoundClassIdentifier(Class<?> teem, String boundClassname) {
+ return boundClassname;
+ }
+
+ public String getSuperclassIdentifier(String classId, String superclassName) {
+ return superclassName;
+ }
+
+ public String getClassIdentifier(Class<?> clazz) {
+ return clazz.getName().replace('.', '/');
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/IClassIdentifierProvider.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/IClassIdentifierProvider.java new file mode 100644 index 000000000..e1fb9a660 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/IClassIdentifierProvider.java @@ -0,0 +1,47 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.runtime;
+
+/**
+ * This class provides globally unique identifiers for all classes.
+ * @author Oliver Frank
+ */
+public interface IClassIdentifierProvider {
+ /**
+ * Returns a globally unique identifier for a base class of a given team.
+ * @param teem
+ * @param boundClassname
+ * @return
+ */
+ public String getBoundClassIdentifier(Class<?> teem, String boundClassname);
+
+ /**
+ * Returns a globally unique identifier for a superclass of the class
+ * with the given id.
+ * @param classId
+ * @param superclassName
+ * @return
+ */
+ public String getSuperclassIdentifier(String classId, String superclassName);
+
+ /**
+ * Returns a globally unique identifier for a class.
+ * @param clazz
+ * @return
+ */
+ public String getClassIdentifier(Class<?> clazz);
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java new file mode 100644 index 000000000..cfab9fbdb --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/runtime/TeamManager.java @@ -0,0 +1,342 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2009, 2012 Oliver Frank and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Oliver Frank - Initial API and implementation + * Stephan Herrmann - Initial API and implementation + **********************************************************************/ +package org.eclipse.objectteams.otredyn.runtime; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass; +import org.objectteams.ITeam; + +/** + * This class administrates the the active teams and their + * callin ids for all joinpoints. + * Teams have to register/unregister themselves at this class, + * while their activation/deactivation. + * The generated client code can call methods of this class to + * get the active teams and callin ids. + * @author Oliver Frank + * + */ +public class TeamManager { + + public static enum TeamStateChange { + REGISTER, + UNREGISTER + } + // void handleTeamStateChange(ITeam t, TeamStateChange stateChange) ; + private static List<List<ITeam>> _teams = new ArrayList<List<ITeam>>(); + private static List<List<Integer>> _callinIds = new ArrayList<List<Integer>>(); + private static Map<String, Integer> joinpointMap = new HashMap<String, Integer>(); + private static Map<Class<?>, List<Integer>> accessIdMap = new HashMap<Class<?>, List<Integer>>(); + private static int currentJoinpointId = 0; + // map all original joinpoints to their inherited versions in subclasses + private static Map<Integer,List<Integer>> joinpointToSubJoinpoints = new HashMap<Integer, List<Integer>>(); + private static IClassRepository classRepository; + + public static void setup(IClassRepository repo) { + classRepository = repo; + } + + /** + * Returns all active teams for joinpoint. + * This method is intended to be called by + * generated client code. + * @param joinpointId + * @return all active teams for the joinpoint or null + * if there are no active teams + */ + public static ITeam[] getTeams(int joinpointId) { + List<ITeam> teams = _teams.get(joinpointId); + if (teams.size() == 0) { + return null; + } + return teams.toArray(new ITeam[teams.size()]); + } + + /** + * Returns all callin ids for the active teams and the joinpoint. + * This method is intended to be called by + * generated client code. + * @param joinpointId + * @return the callin ids for the joinpoint or null + * if there are no active teams + */ + public static int[] getCallinIds(int joinpointId) { + List<Integer> callinIds = _callinIds.get(joinpointId); + if (callinIds.size() == 0) { + return null; + } + int[] result = new int[callinIds.size()]; + int i = 0; + for (Integer callinId : callinIds) { + result[i] = callinId; + i++; + } + return result; + } + + /** + * Returns the member id for a given team and a access id + * used in this team. + * This method is intended to be called by + * generated client code. + * @param accessId + * @param Team + * @return + */ + public static int getMemberId(int accessId, Class<? extends ITeam> teamClass) { + return accessIdMap.get(teamClass).get(accessId); + } + + /** + * Returns and possibly creates a globally unique id for a + * joinpoint identifier. Additionally it prepares the + * structures needed to store the teams and callin ids for a joinpoint. + * @param joinpointIdentifier + * @return a joinpoint id + */ + public synchronized static int getJoinpointId(String joinpointIdentifier) { + Integer joinpointId = getExistingJoinpointId(joinpointIdentifier); + if (joinpointId == null) { + joinpointMap.put(joinpointIdentifier, currentJoinpointId); + List<ITeam> teams = new ArrayList<ITeam>(); + _teams.add(teams); + List<Integer> callinIds = new ArrayList<Integer>(); + _callinIds.add(callinIds); + return currentJoinpointId++; + } + return joinpointId; + } + + /** + * Returns an existing globally unique joinpoint id + * for a joinpoint identifier + * @param joinpointIdentifier + * @return the joinpoint id or null if a joinpoint id + * doesn't exist for this joinpoint identifier + */ + private static Integer getExistingJoinpointId(String joinpointIdentifier) { + return joinpointMap.get(joinpointIdentifier); + } + + /** + * Handles registration and unregistration of teams. + * Stores the team and the ids dependend on the joinpoints and + * tell the {@link AbstractBoundClass} representing the base classes + * of the team, that there is new Binding + * @param t + * @param stateChange + */ + public static void handleTeamStateChange(ITeam t, TeamManager.TeamStateChange stateChange) { + IClassIdentifierProvider provider = ClassIdentifierProviderFactory.getClassIdentifierProvider(); + Class<? extends ITeam> teamClass = t.getClass(); + String teamId = provider.getClassIdentifier(teamClass); + IBoundTeam teem = classRepository.getTeam(teamClass.getName(), teamId, teamClass.getClassLoader()); + + for (IBinding binding : teem.getBindings()) { + String boundClassName = binding.getBoundClass(); + String boundClassIdentifier = provider.getBoundClassIdentifier(teamClass, boundClassName); + // FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings: + IBoundClass boundClass = classRepository.getBoundClass(boundClassName.replace('/', '.'), boundClassIdentifier, teamClass.getClassLoader()); + // FIXME(SH): if boundClass is a role we need to find tsub roles, too! + switch (binding.getType()) { + case CALLIN_BINDING: + IMethod method = boundClass.getMethod(binding.getMemberName(), binding.getMemberSignature(), binding.isHandleCovariantReturn()); + int joinpointId = getJoinpointId(boundClass + .getMethodIdentifier(method)); + synchronized (method) { + changeTeamsForJoinpoint(t, binding.getCallinId(), joinpointId, stateChange); + List<Integer> subJoinpoints = joinpointToSubJoinpoints.get(joinpointId); + if (subJoinpoints != null) + for (Integer subJoinpoint : subJoinpoints) + changeTeamsForJoinpoint(t, binding.getCallinId(), subJoinpoint, stateChange); + } + boundClass.handleAddingOfBinding(binding); // TODO(SH): more lazy + break; + } + + } + } + public static void handleTeamLoaded(Class<? extends ITeam> teamClass) { + if (teamClass != null) + handleDecapsulation(teamClass); + performPendingTask(); + } + private static void handleDecapsulation(Class<? extends ITeam> teamClass) { + IClassIdentifierProvider provider = ClassIdentifierProviderFactory.getClassIdentifierProvider(); + String teamId = provider.getClassIdentifier(teamClass); + IBoundTeam teem = classRepository.getTeam(teamClass.getName(), teamId, teamClass.getClassLoader()); + + for (IBinding binding : teem.getBindings()) { + String boundClassName = binding.getBoundClass(); + String boundClassIdentifier = provider.getBoundClassIdentifier(teamClass, boundClassName.replace('.', '/')); + // FIXME(SH): the following may need adaptation for OT/Equinox or other multi-classloader settings: + IBoundClass boundClass = classRepository.getBoundClass(boundClassName.replace('/', '.'), boundClassIdentifier, teamClass.getClassLoader()); + + switch (binding.getType()) { + case FIELD_ACCESS: // fallthrough + case METHOD_ACCESS: + IMember member = null; + if (binding.getType() == IBinding.BindingType.FIELD_ACCESS) { + member = boundClass.getField(binding.getMemberName(), + binding.getMemberSignature()); + } else { + member = boundClass.getMethod(binding.getMemberName(), binding.getMemberSignature(), false/*covariantReturn*/); + } + + int memberId = member.getId(boundClass); + synchronized (member) { + addAccessIds(teamClass, teem, binding.getCallinId(), memberId); + } + boundClass.handleAddingOfBinding(binding); + break; + } + } + } + /** + * Stores the access ids of a team and the corresponding member ids + * @param t + * @param teem + * @param accessId + * @param memberId + * @param stateChange + */ + private static void addAccessIds(Class<? extends ITeam> clazz, IBoundTeam teem, int accessId, int memberId) { + List<Integer> accessIds = accessIdMap.get(clazz); + if (accessIds == null) { + int highestAccessId = teem.getHighestAccessId() + 1; + accessIds = new ArrayList<Integer>(highestAccessId); + for (int i = 0; i <= highestAccessId; i++) { + accessIds.add(0); + } + accessIdMap.put(clazz, accessIds); + } + accessIds.set(accessId, memberId); + } + + /** + * Stores or removes the team + * @param t + * @param callinId + * @param joinpointId + * @param stateChange + */ + private static void changeTeamsForJoinpoint(ITeam t, int callinId, int joinpointId, TeamManager.TeamStateChange stateChange) { + switch (stateChange) { + case REGISTER: + List<ITeam> teams = _teams.get(joinpointId); + teams.add(0, t); + List<Integer> callinIds = _callinIds.get(joinpointId); + callinIds.add(0, callinId); + break; + case UNREGISTER: + teams = _teams.get(joinpointId); + callinIds = _callinIds.get(joinpointId); + + int index = teams.indexOf(t); + while (index > -1) { + teams.remove(index); + callinIds.remove(index); + index = teams.indexOf(t); + } + break; + default: throw new RuntimeException("Unknown team state change: " + stateChange.name()); + } + } + + /** + * Merge the teams and callin ids of two joinpoints into the second. + * This is used so that activating teams for the second joinpoint (subclass) + * will include the effect of activation of the former (superclass). + * @param superClass + * @param subClass + * @param superMethod + * @param subMethod + */ + public static void mergeJoinpoints(IBoundClass superClass, + IBoundClass subClass, final IMethod superMethod, IMethod subMethod, final boolean handleCovariantReturn) { + if (subClass.isAnonymous()) { + // destClass is a placeholder, schedule real merging for when a real subclass gets wired: + subClass.addWiringTask(new ISubclassWiringTask() { + public void wire(IBoundClass superClass, IBoundClass subClass) { + IMethod subMethod = subClass.getMethod(superMethod.getName(), superMethod.getSignature(), + handleCovariantReturn); + mergeJoinpoints(superClass, subClass, superMethod, subMethod, handleCovariantReturn); + } + }); + return; + } + synchronized (subClass) { + Integer superJoinpointId = getJoinpointId(superClass.getMethodIdentifier(superMethod)); + if (superJoinpointId != null) { + int subJoinpointId = getJoinpointId(subClass.getMethodIdentifier(subMethod)); + + List<Integer> subJoinpoints = joinpointToSubJoinpoints.get(superJoinpointId); + if (subJoinpoints == null) { + subJoinpoints = new ArrayList<Integer>(); + joinpointToSubJoinpoints.put(superJoinpointId, subJoinpoints); + } + // already processed? + if (!subJoinpoints.contains(subJoinpointId)) { + subJoinpoints.add(subJoinpointId); + applyJoinpointMerge(superJoinpointId, subJoinpointId); + } + } + } + } + + private synchronized static void applyJoinpointMerge(Integer srcJoinpointId, int destJoinpointId) { + List<ITeam> teams = _teams.get(destJoinpointId); + List<Integer> callinIds = _callinIds.get(destJoinpointId); + List<ITeam> srcTeams = _teams.get(srcJoinpointId); + List<Integer> srcCallins = _callinIds.get(srcJoinpointId); + for (int s=0; s<srcTeams.size(); s++) { + int d = 0; // FIXME(SH): find insertion index based on activation priority!! + teams.add(d, srcTeams.get(s)); + callinIds.add(0, srcCallins.get(s)); + } + _teams.set(destJoinpointId, teams); + _callinIds.set(destJoinpointId, callinIds); + } + + /** + * A "mailbox" for pending tasks (per thread). + * The weaver stores redefinition tasks here that failed because the class + * was in a state between load-time transformation and definition. + */ + public static ThreadLocal<Runnable> pendingTasks = new ThreadLocal<Runnable>(); + /** + * Perform any tasks that are pending for this thread: redefinitions of a class + * that was in a state between load-time transformation and definition. + */ + public static void performPendingTask() { + Runnable task = pendingTasks.get(); + if (task != null) { +// System.out.println("pending "+task); + pendingTasks.set(null); + try { + task.run(); + } catch (Throwable t) { + System.err.println(t+" because "+t.getCause()); + pendingTasks.set(task); // no success try later ?? + } + } + } +} diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ClassNames.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ClassNames.java new file mode 100644 index 000000000..a4bb5b715 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ClassNames.java @@ -0,0 +1,49 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.transformer.names;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.objectteams.otredyn.runtime.TeamManager;
+import org.objectteams.IBoundBase;
+import org.objectteams.ITeam;
+import org.objectteams.ImplicitTeamActivation;
+import org.objectteams.SneakyException;
+import org.objectteams.Team;
+
+/**
+ * Container for class names used in the bytecode manipulating classes
+ * @author Oliver Frank
+ */
+public abstract class ClassNames {
+ public final static String I_BOUND_BASE_SLASH = IBoundBase.class.getName().replace('.', '/');
+ public final static String OBJECT_SLASH = Object.class.getName().replace('.', '/');
+ public final static String CLASS_SLASH = Class.class.getName().replace('.', '/');
+ public final static String I_BOUND_BASE_DOT = IBoundBase.class.getName();
+ public final static String TEAM_MANAGER_SLASH = TeamManager.class.getName().replace('.', '/');
+ public final static String ITEAM_SLASH = ITeam.class.getName().replace('.', '/');
+ public final static String TEAM_SLASH = Team.class.getName().replace('.', '/');
+ public final static String LIST_SLASH = List.class.getName().replace('.', '/');
+ public final static String HASH_SET_SLASH = HashSet.class.getName().replace('.', '/');
+ public final static String IMPLICIT_ACTIVATION = ImplicitTeamActivation.class.getName().replace('.', '/');
+
+ public static final String SNEAKY_EXCEPTION_SLASH = SneakyException.class.getName().replace('.', '/');
+ // member of SneakyException:
+ public static final String RETHROW_SELECTOR = "rethrow";
+ public static final String RETHROW_SIGNATURE = "()V";
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ConstantMembers.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ConstantMembers.java new file mode 100644 index 000000000..72eec7343 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/transformer/names/ConstantMembers.java @@ -0,0 +1,123 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.transformer.names;
+
+import org.eclipse.objectteams.otredyn.bytecode.Field;
+import org.eclipse.objectteams.otredyn.bytecode.Method;
+import org.eclipse.objectteams.otredyn.bytecode.Types;
+import org.objectweb.asm.Opcodes;
+
+
+
+/**
+ * Container for methods used in the bytecode manipulating classes
+ * @author Oliver Frank
+ */
+public abstract class ConstantMembers {
+
+ // predefined field:
+ public static final String OT_ROLE_SET = "_OT$roleSet";
+ public static final String HASH_SET_FIELD_TYPE = Types
+ .getTypeStringForField(Types.getAsType(ClassNames.HASH_SET_SLASH));
+ public static final Field roleSet = new Field(OT_ROLE_SET, HASH_SET_FIELD_TYPE);
+
+ // predefined methods
+ public static final Method callOrig = new Method("_OT$callOrig", Types
+ .getTypeStringForMethod(Types.getAsType(ClassNames.OBJECT_SLASH),
+ new String[] { Types.INT,
+ Types.getAsArrayType(ClassNames.OBJECT_SLASH) }));
+
+ public static final Method callOrigStatic = new Method(
+ "_OT$callOrigStatic",
+ Types.getTypeStringForMethod(Types
+ .getAsType(ClassNames.OBJECT_SLASH), new String[] {
+ Types.INT, Types.getAsArrayType(ClassNames.OBJECT_SLASH) }),
+ true, Opcodes.ACC_PUBLIC);
+
+ // this method's signature actually depends on the enclosing team class
+ // used when generating callOrigStatic method into a role class
+ // which needs two synthetic arguments.
+ public static Method callOrigStaticRoleVersion(String teamClass) {
+ return new Method(
+ "_OT$callOrigStatic",
+ Types.getTypeStringForMethod(
+ Types.getAsType(ClassNames.OBJECT_SLASH),
+ new String[] {
+ Types.INT,
+ Types.getAsType(teamClass),
+ Types.INT,
+ Types.getAsArrayType(ClassNames.OBJECT_SLASH)
+ }),
+ true, Opcodes.ACC_PUBLIC);
+ }
+
+ public static final Method callAllBindingsClient = new Method(
+ "callAllBindings", Types.getTypeStringForMethod(Types
+ .getAsType(ClassNames.OBJECT_SLASH), new String[] {
+ Types.INT, Types.getAsArrayType(ClassNames.OBJECT_SLASH) }));
+
+ public static final Method callAllBindingsTeam = new Method(
+ "_OT$callAllBindings", Types.getTypeStringForMethod(Types
+ .getAsType(ClassNames.OBJECT_SLASH), new String[] {
+ Types.getAsType(ClassNames.I_BOUND_BASE_SLASH),
+ Types.getAsArrayType(ClassNames.ITEAM_SLASH), Types.INT,
+ Types.getAsArrayType(Types.INT), Types.INT,
+ Types.getAsArrayType(ClassNames.OBJECT_SLASH) }));
+
+ public static final Method access = new Method("_OT$access", Types
+ .getTypeStringForMethod(Types.getAsType(ClassNames.OBJECT_SLASH),
+ new String[] { Types.INT, Types.INT,
+ Types.getAsArrayType(ClassNames.OBJECT_SLASH),
+ Types.getAsType(ClassNames.ITEAM_SLASH) }));
+
+ public static final Method accessStatic = new Method("_OT$accessStatic", Types
+ .getTypeStringForMethod(Types.getAsType(ClassNames.OBJECT_SLASH),
+ new String[] { Types.INT, Types.INT,
+ Types.getAsArrayType(ClassNames.OBJECT_SLASH),
+ Types.getAsType(ClassNames.ITEAM_SLASH) }), true, Opcodes.ACC_PUBLIC);
+
+ public static final Method getTeams = new Method("getTeams", Types
+ .getTypeStringForMethod(
+ Types.getAsArrayType(ClassNames.ITEAM_SLASH),
+ new String[] { Types.INT }));
+
+ public static final Method getCallinIds = new Method("getCallinIds", Types
+ .getTypeStringForMethod(Types.getAsArrayType(Types.INT),
+ new String[] { Types.INT }));
+
+ public static final Method getMemberId = new Method("getMemberId", Types
+ .getTypeStringForMethod(Types.INT, new String[] { Types.INT,
+ Types.getAsType(ClassNames.CLASS_SLASH) }));
+
+ public static final Method addOrRemoveRole = new Method("_OT$addOrRemoveRole", Types
+ .getTypeStringForMethod(Types.VOID, new String[] { Types.getAsType(ClassNames.OBJECT_SLASH), Types.BOOLEAN }));
+
+ public static boolean isReflectiveOTMethod(String methodName, String methodDescriptor) {
+ if ((methodName.equals("hasRole") && methodDescriptor.equals("(Ljava/lang/Object;)Z"))
+ || (methodName.equals("hasRole") && methodDescriptor.equals("(Ljava/lang/Object;Ljava/lang/Class;)Z"))
+ || (methodName.equals("getRole") && methodDescriptor.equals("(Ljava/lang/Object;)Ljava/lang/Object;"))
+ || (methodName.equals("getRole") && methodDescriptor.equals("(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;"))
+ || (methodName.equals("getAllRoles") && methodDescriptor.equals("()[Ljava/lang/Object;"))
+ || (methodName.equals("getAllRoles") && methodDescriptor.equals("(Ljava/lang/Class;)[Ljava/lang/Object;"))
+ || (methodName.equals("unregisterRole") && methodDescriptor.equals("(Ljava/lang/Object;)V"))
+ || (methodName.equals("unregisterRole") && methodDescriptor.equals("(Ljava/lang/Object;Ljava/lang/Class;)V"))
+ )
+ return true;
+ return false;
+ }
+
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/util/ArrayUtil.java b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/util/ArrayUtil.java new file mode 100644 index 000000000..7f3b84119 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/eclipse/objectteams/otredyn/util/ArrayUtil.java @@ -0,0 +1,47 @@ +/**********************************************************************
+ * This file is part of "Object Teams Dynamic Runtime Environment"
+ *
+ * Copyright 2009, 2012 Oliver Frank and others.
+ *
+ * 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
+ *
+ * Please visit http://www.eclipse.org/objectteams for updates and contact.
+ *
+ * Contributors:
+ * Oliver Frank - Initial API and implementation
+ * Stephan Herrmann - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.objectteams.otredyn.util;
+
+
+public abstract class ArrayUtil {
+
+ public static String[] stringArrayPrepend(String[] array,String item) {
+
+ if (array == null) {
+ return new String[] { item } ;
+ }
+
+ int arr_len = array.length;
+ String [] new_arr = new String[array.length+1];
+ System.arraycopy(array,0,new_arr,1,arr_len);
+ new_arr[0] = item;
+ return new_arr;
+ }
+
+ public static String[] stringArrayAppend(String[] array,String item) {
+
+ if (array == null) {
+ return new String[] { item } ;
+ }
+
+ int arr_len = array.length;
+ String [] new_arr = new String[array.length+1];
+ System.arraycopy(array,0,new_arr,0,arr_len);
+ new_arr[arr_len] = item;
+ return new_arr;
+ }
+}
diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/IBoundBase.java b/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/IBoundBase.java new file mode 100644 index 000000000..afc892ab8 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/IBoundBase.java @@ -0,0 +1,42 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2007, 2012 Berlin Institute of Technology, Germany, and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + * Oliver Frank - Initial API and Implementation + **********************************************************************/ +package org.objectteams; + +/** Super type for all bound base classes. Purely internal class, not intended for client use. */ +public interface IBoundBase { + + /** + * Call a bound base method identified by its ID. + * @param boundMethod_id globally unique ID of a bound base method + * @param args packed arguments (incl. boxing) + * @return (possibly boxed) result of the bound base method. + */ + Object _OT$callOrig(int boundMethod_id, Object[] args); + + /** + * Provides access to fields and methods of a base class + * that have package, protected or private visbility + * @param accessId Unique identifier in the class for the field or method + * @param opKind 0 for read access, 1 for write access. only used for fields + * @param args arguments for a method + * @return + */ + Object _OT$access(int accessId, int opKind, Object[] args, Team caller); + + /** Method to be used by generated code, only (lifting constructor & unregisterRole()). */ + void _OT$addOrRemoveRole(Object aRole, boolean adding); +}
\ No newline at end of file diff --git a/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/Team.java b/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/Team.java new file mode 100644 index 000000000..b59aaea59 --- /dev/null +++ b/plugins/org.eclipse.objectteams.otredyn/src/org/objectteams/Team.java @@ -0,0 +1,655 @@ +/********************************************************************** + * This file is part of "Object Teams Dynamic Runtime Environment" + * + * Copyright 2002, 2012 Berlin Institute of Technology, Germany, and others. + * + * 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 + * + * Please visit http://www.eclipse.org/objectteams for updates and contact. + * + * Contributors: + * Berlin Institute of Technology - Initial API and implementation + * Oliver Frank - Initial API and Implementation + **********************************************************************/ +package org.objectteams; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.WeakHashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.eclipse.objectteams.otredyn.runtime.TeamManager; + + +/** + * 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>() { + @Override + 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 (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 activation 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 final boolean isActive() { + return isActive(Thread.currentThread()); + } + + /** + * {@inheritDoc} + */ + public final boolean isActive(Thread thread) { + 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) { + // register the team at the TeamManager + TeamManager.handleTeamStateChange(this, TeamManager.TeamStateChange.REGISTER); + _OT$registrationState = _OT$REGISTERED; + } + } + + private void doUnregistration() { + if (_OT$registrationState == _OT$REGISTERED) { + // unregister the team at the TeamManager + TeamManager.handleTeamStateChange(this, TeamManager.TeamStateChange.UNREGISTER); + _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) throws IllegalArgumentException { + // 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) throws IllegalArgumentException { + // 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) throws IllegalArgumentException { + // 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) throws IllegalArgumentException { + // 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) + + /** + * If a serializable team wishes to persist its global activation status it must + * call this method from its writeObject() method and correspondingly call + * {@link #readGlobalActivationState(ObjectInputStream)} from its readObject(). + */ + protected void writeGlobalActivationState(ObjectOutputStream out) throws IOException { + out.writeBoolean(this._OT$globalActive); + } + /** + * If a serializable team wishes to persist its global activation status it must + * call this method from its readObject() method and correspondingly call + * {@link #writeGlobalActivationState(ObjectOutputStream)} from its writeObject(). + * If a team is restored that was globally active when serialized, it will be activated + * correspondingly during deserialization when this method is called. + */ + protected void readGlobalActivationState(ObjectInputStream in) throws IOException { + this._OT$globalActive = in.readBoolean(); + if (this._OT$globalActive) { + this._OT$lazyGlobalActiveFlag = true; + this.doRegistration(); + } + } + /** + * Serializable teams must invoke this method once from their readObject() method + * in order to re-initialize internal data structures. + */ + protected void restore() { /* empty; implementation will be generated for each serializable sub-class. */ } + /** + * Serializable teams must invoke this method from their readObject() method + * for each role that has been retrieved and shall be re-registered for this team. + */ + protected void restoreRole(Class<?> clazz, Object role) { /* empty; implementation will be generated for each serializable sub-class. */ } + + /** + * This method is the first part of the new chaining wrapper. + * It should be called from the generated client code. + * + * @param baze the current base object + * @param teams the current team objects + * @param idx the index of the current team in teams + * @param callinIds an array of ids, that are unique in the team + * for a base method in a base class + * @param boundMethodId an unique id for a base method in the base class. + * This id is needed for a base call. + * @param args packed arguments. + * @return possibly boxed result + */ + public Object _OT$callAllBindings(IBoundBase baze, ITeam[] teams,int idx,int[] callinIds, int boundMethodId, Object[] args) + { + + + this._OT$callBefore(baze, callinIds[idx], boundMethodId, args); + + Object res = this._OT$callReplace(baze, teams, idx, callinIds, boundMethodId, args); + + this._OT$callAfter(baze, callinIds[idx], boundMethodId, args, res); // make result available to param mappings! + + return res; + } + + /** + * This method calls the next team or a base method, + * if there are no more active teams for a joinpoint + * + * @param baze the current base object + * @param teams the current base object + * @param idx the index of the current team in teams, also points into callinIds, i.e., both lists run synchroneously. + * @param callinIds an array of ids, that are unique in the team + * for a base method in a base class + * @param boundMethodId an unique id for a base method in the base class. + * This id is needed for a base call. + * @param args original packed arguments. + * @param baseCallArgs packed arguments as provided to the base call. + * @return possibly boxed result + */ + public Object _OT$callNext(IBoundBase baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args, Object[] baseCallArgs) + { +//System.out.println("callNext idx="+idx); +//for (Team t : teams) +// System.out.println("team="+t); + // Are there still active teams? + if (idx+1 < teams.length) { + // Yes, so call the next team/callin + return teams[idx+1]._OT$callAllBindings(baze, teams, idx+1, callinIds, boundMethodId, args); + } else { + //No, call the base method + if (baze == null) { + //handle base call to a static base method + return teams[idx]._OT$callOrigStatic(callinIds[idx], boundMethodId, args); + } else { +//System.out.println("callOrig: "+boundMethodId); + return baze._OT$callOrig(boundMethodId, args); +//Object result = baze.callOrig(boundMethodId, args); +//System.out.println("->"+result); +//return result; + } + } + } + + /** + * Executes all before callins for a given callin id. + * Must be overridden by a team, if the team gets replace callins. + * + * @param baze the current base object + * @param callinId the current callin id + * @param boundMethodId an unique id for a base method in the base class. + * This id is needed for a base call. + * @param args packed arguments. + * @return possibly boxed result + */ + public void _OT$callBefore(IBoundBase baze, int callinId, int boundMethodId, Object[] args) { + // nop; override with code from before callin bindings. + } + + /** + * Executes all after callins for a given callin id. + * Must be overridden by a team, if the team gets replace callins. + * + * @param baze the current base object + * @param callinId the current callin id + * @param boundMethodId an unique id for a base method in the base class. + * This id is needed for a base call. + * @param args packed arguments. + * @param result the result of the base method. Could be used by after callins + */ + public void _OT$callAfter(IBoundBase baze, int callinId, int boundMethodId, Object[] args, Object result) { + // nop; override with code from after callin bindings. + } + + /** + * Execute replace callins of the team for the current callin id. + * Must be overridden by a team, if the team has got replace callins. + * + * @param baze the current base object + * @param teams the current base object + * @param idx the index of the current team in teams + * @param callinIds an array of ids, that are unique in the team + * for a base method in a base class + * @param boundMethodId an unique id for a base method in the base class. + * This id is needed for a base call. + * @param args packed arguments. + * @return possibly boxed result + */ + public Object _OT$callReplace(IBoundBase baze, ITeam[] teams, int idx, int[] callinIds, int boundMethodId, Object[] args) { + // default; override with code from replace callin bindings. + return _OT$callNext(baze, teams, idx, callinIds, boundMethodId, args, null); + } + + /** + * Calls the method callOrigStatic of a concrete class dependend on the + * given callin id. + * Must be overridden in a team, if the team has got base calls + * to static base methods + * @param callinId + * @param boundMethodId + * @param args + * @return + */ + public Object _OT$callOrigStatic(int callinId, int boundMethodId, Object[] args) { + return null; + } +} |