Skip to content

Commit

Permalink
[lang][core] Generate inner class for context aware capacity calls.
Browse files Browse the repository at this point in the history
see #459

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Feb 21, 2017
1 parent 9db3d71 commit 5113a2d
Show file tree
Hide file tree
Showing 12 changed files with 833 additions and 6 deletions.
Expand Up @@ -29,5 +29,58 @@
* @mavenartifactid $ArtifactId$
*/
public interface Capacity {
//

/** Wrapper to a capacity that enable to manage and provide the caller to the capacity function.
*
* @param <C> the type of the wrapper capacity.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 0.5
*/
abstract class ContextAwareCapacityWrapper<C extends Capacity> implements Capacity {

private static final ThreadLocal<Object> CALLER = new ThreadLocal<>();

/** The wrapped capacity.
*/
protected final C capacity;

private final Object caller;

/** Constructor.
*
* @param capacity the wrapped capacity.
* @param caller the owner of the wrapper, that should be the caller of the capacity's function.
*/
public ContextAwareCapacityWrapper(C capacity, Object caller) {
assert capacity != null;
this.capacity = capacity;
this.caller = caller;
}

/** Ensure that the local-thread variable stores the caller.
*/
protected final void ensureCallerInLocalThread() {
CALLER.set(this.caller);
}

/** Reset the local-thread variable storing the caller.
*/
@SuppressWarnings("static-method")
protected final void resetCallerInLocalThread() {
CALLER.remove();
}

/** Replies the caller of the capacity functions.
*
* @return the caller, or {@code null} if no caller is registered.
*/
public static final Object getCaller() {
return CALLER.get();
}

}

}
Expand Up @@ -50,6 +50,10 @@ public class InferredStandardParameter {
*/
protected String defaultValueAnnotationValue;

/** Basename of default value annotation value.
*/
protected String defaultValueAnnotationValueBasename;

/**
* @param source - the original parameter.
* @param name - the name of the formal parameter.
Expand Down Expand Up @@ -98,12 +102,22 @@ public String getDefaultValueAnnotationValue() {
return this.defaultValueAnnotationValue;
}

/** Replies the basename of the value of the annotation that is marked this parameter with a default value.
*
* @return the basename of the annotation's value.
*/
public String getDefaultValueAnnotationValueBasename() {
return this.defaultValueAnnotationValueBasename;
}

/** Set the value of the annotation that is marked this parameter with a default value.
*
* @param value the annotation's value.
* @param basename the basename of the value.
*/
void setDefaultValueAnnotationValue(String value) {
void setDefaultValueAnnotationValue(String value, String basename) {
this.defaultValueAnnotationValue = value;
this.defaultValueAnnotationValueBasename = basename;
}

}
Expand Up @@ -110,6 +110,7 @@
import org.eclipse.xtext.xbase.lib.Pair;
import org.eclipse.xtext.xbase.lib.Procedures;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure2;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.typesystem.InferredTypeIndicator;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;
Expand Down Expand Up @@ -1022,6 +1023,88 @@ protected void initialize(SarlCapacity source, JvmGenericType inferredJvmType) {
} finally {
closeContext(context);
}

// Generate the internal class for context-aware wrappers

// Change the modifiers on the generated type.
final JvmGenericType innerType = this.typesFactory.createJvmGenericType();
innerType.setInterface(false);
innerType.setAbstract(false);
innerType.setVisibility(JvmVisibility.PUBLIC);
innerType.setStatic(true);
innerType.setStrictFloatingPoint(false);
innerType.setFinal(false);
final String innerTypeName = Capacity.ContextAwareCapacityWrapper.class.getSimpleName();
innerType.setSimpleName(innerTypeName);

inferredJvmType.getMembers().add(innerType);

final JvmTypeParameter typeParameter = this.typesFactory.createJvmTypeParameter();
typeParameter.setName("C"); //$NON-NLS-1$
final JvmUpperBound constraint = this.typesFactory.createJvmUpperBound();
constraint.setTypeReference(this._typeReferenceBuilder.typeRef(inferredJvmType));
typeParameter.getConstraints().add(constraint);
innerType.getTypeParameters().add(typeParameter);

final JvmTypeReference extendedType = inferredJvmType.getExtendedInterfaces().iterator().next();
final JvmTypeReference superType = this._typeReferenceBuilder.typeRef(
extendedType.getQualifiedName() + "$" + innerTypeName, //$NON-NLS-1$
this._typeReferenceBuilder.typeRef(typeParameter));
innerType.getSuperTypes().add(superType);

innerType.getSuperTypes().add(this._typeReferenceBuilder.typeRef(inferredJvmType));

final JvmConstructor constructor = this.typesFactory.createJvmConstructor();
constructor.setVisibility(JvmVisibility.PUBLIC);
innerType.getMembers().add(constructor);
final JvmFormalParameter parameter1 = this.typesFactory.createJvmFormalParameter();
parameter1.setName("capacity"); //$NON-NLS-1$
parameter1.setParameterType(this._typeReferenceBuilder.typeRef(typeParameter));
constructor.getParameters().add(parameter1);
final JvmFormalParameter parameter2 = this.typesFactory.createJvmFormalParameter();
parameter2.setName("caller"); //$NON-NLS-1$
parameter2.setParameterType(this._typeReferenceBuilder.typeRef(Object.class));
constructor.getParameters().add(parameter2);
setBody(constructor, (it) -> {
it.append("super(capacity, caller);"); //$NON-NLS-1$
});

final Set<ActionPrototype> createdActions = new TreeSet<>();
for (final JvmGenericType sourceType : Iterables.concat(
Collections.singletonList(inferredJvmType),
Iterables.transform(Iterables.skip(inferredJvmType.getExtendedInterfaces(), 1), (it) -> {
return (JvmGenericType) it.getType();
}))) {
copyNonStaticPublicJvmOperations(sourceType, innerType, createdActions, (operation, it) -> {
it.append("try {"); //$NON-NLS-1$
it.newLine();
it.append(" ensureCallerInLocalThread();"); //$NON-NLS-1$
it.newLine();
it.append(" "); //$NON-NLS-1$
if (operation.getReturnType() != null && !Objects.equal("void", operation.getReturnType().getIdentifier())) { //$NON-NLS-1$
it.append("return "); //$NON-NLS-1$
}
it.append("this.capacity."); //$NON-NLS-1$
it.append(operation.getSimpleName());
it.append("("); //$NON-NLS-1$
boolean first = true;
for (final JvmFormalParameter fparam : operation.getParameters()) {
if (first) {
first = false;
} else {
it.append(", "); //$NON-NLS-1$
}
it.append(fparam.getName());
}
it.append(");"); //$NON-NLS-1$
it.newLine();
it.append("} finally {"); //$NON-NLS-1$
it.newLine();
it.append(" resetCallerInLocalThread();"); //$NON-NLS-1$
it.newLine();
it.append("}"); //$NON-NLS-1$
});
}
}

/** Initialize the SARL space type.
Expand Down Expand Up @@ -2763,4 +2846,55 @@ private String reentrantSerialize(EObject object) {
return code;
}

/** Copy the JVM operations from the source to the destination.
*
* @param source the source.
* @param target the destination.
* @param createdActions the set of actions that are created before (input) or during (output) the invocation.
* @param bodyBuilder the builder of the target's operations.
* @since 0.5
*/
protected void copyNonStaticPublicJvmOperations(JvmGenericType source, JvmGenericType target,
Set<ActionPrototype> createdActions, Procedure2<JvmOperation, ITreeAppendable> bodyBuilder) {
final Iterable<JvmOperation> operations = Iterables.transform(Iterables.filter(source.getMembers(), (it) -> {
if (it instanceof JvmOperation) {
final JvmOperation op = (JvmOperation) it;
return !op.isStatic() && op.getVisibility() == JvmVisibility.PUBLIC;
}
return false;
}), (it) -> (JvmOperation) it);
for (final JvmOperation operation : operations) {
final ActionParameterTypes types = this.sarlSignatureProvider.createParameterTypesFromJvmModel(
operation.isVarArgs(), operation.getParameters());
final ActionPrototype actSigKey = this.sarlSignatureProvider.createActionPrototype(
operation.getSimpleName(), types);
if (createdActions.add(actSigKey)) {
final JvmOperation newOp = this.typesFactory.createJvmOperation();
target.getMembers().add(newOp);
newOp.setAbstract(false);
newOp.setDefault(operation.isDefault());
newOp.setDeprecated(operation.isDeprecated());
newOp.setFinal(false);
newOp.setNative(false);
newOp.setReturnType(this.typeBuilder.cloneWithProxies(operation.getReturnType()));
newOp.setSimpleName(operation.getSimpleName());
newOp.setStatic(false);
newOp.setStrictFloatingPoint(operation.isStrictFloatingPoint());
newOp.setSynchronized(operation.isSynchronized());
newOp.setVisibility(JvmVisibility.PUBLIC);

for (final JvmFormalParameter parameter : operation.getParameters()) {
final JvmFormalParameter newParam = this.typesFactory.createJvmFormalParameter();
newOp.getParameters().add(newParam);
newParam.setName(parameter.getSimpleName());
newParam.setParameterType(this.typeBuilder.cloneWithProxies(parameter.getParameterType()));
}

newOp.setVarArgs(operation.isVarArgs());

setBody(newOp, (it) -> bodyBuilder.apply(operation, it));
}
}
}

}
Expand Up @@ -40,6 +40,7 @@
import io.sarl.lang.core.Skill;
import io.sarl.lang.core.SpaceID;
import io.sarl.lang.core.UnimplementedCapacityException;
import io.sarl.lang.core.tests.core.AbstractAgentTraitBehaviorTest.Capacity1;
import io.sarl.tests.api.AbstractSarlTest;
import io.sarl.tests.api.Nullable;

Expand Down Expand Up @@ -111,7 +112,7 @@ public void getSkill_set() throws Exception {
//
Object result = invoke(getInstance(), "getSkill", Capacity1.class);
//
assertSame(skill, result);
assertInstanceOf(Capacity1.class, result);
}

@Test
Expand Down Expand Up @@ -139,7 +140,7 @@ public void setSkill() throws Exception {
assertSame(skill, result);
//
result = invoke(getInstance(), "getSkill", Capacity1.class);
assertSame(skill, result);
assertInstanceOf(Capacity1.class, result);
}

@Test
Expand All @@ -149,7 +150,7 @@ public void operator_mappedTo() throws Exception {
invoke(getInstance(), "operator_mappedTo", Capacity1.class, skill);
//
Object result = invoke(getInstance(), "getSkill", Capacity1.class);
assertSame(skill, result);
assertInstanceOf(Capacity1.class, result);
}

@Test(expected = UnimplementedCapacityException.class)
Expand Down Expand Up @@ -235,7 +236,11 @@ public <S extends Skill> S setSkill_Fake(S skill, Class<? extends Capacity>... c
* @mavenartifactid $ArtifactId$
*/
protected static interface Capacity1 extends Capacity {
//
public static class ContextAwareCapacityWrapper<C extends Capacity1> extends Capacity.ContextAwareCapacityWrapper<C> implements Capacity1 {
public ContextAwareCapacityWrapper(C capacity, Object caller) {
super(capacity, caller);
}
}
}

/**
Expand Down
78 changes: 78 additions & 0 deletions tests/io.sarl.lang.tests/src/io/sarl/lang/tests/bugs/Bug294.java
Expand Up @@ -151,6 +151,84 @@ public void testCompiler() throws Exception {
" @DefaultValueUse(\"java.lang.String,float,java.lang.Object*\")",
" @SyntheticMember",
" public abstract void influenceSteering(final String linearInfluence, final Object... otherInfluences);",
" ",
" public static class ContextAwareCapacityWrapper<C extends PhysicEnvironment> extends Capacity.ContextAwareCapacityWrapper<C> implements PhysicEnvironment {",
" public ContextAwareCapacityWrapper(final C capacity, final Object caller) {",
" super(capacity, caller);",
" }",
" ",
" public void influenceKinematic(final String linearInfluence, final float angularInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceKinematic(linearInfluence, angularInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceSteering(final String linearInfluence, final float angularInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceSteering(linearInfluence, angularInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceKinematic(final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceKinematic(otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceKinematic(final float angularInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceKinematic(angularInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceKinematic(final String linearInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceKinematic(linearInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceSteering(final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceSteering(otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceSteering(final float angularInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceSteering(angularInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" ",
" public void influenceSteering(final String linearInfluence, final Object... otherInfluences) {",
" try {",
" ensureCallerInLocalThread();",
" this.capacity.influenceSteering(linearInfluence, otherInfluences);",
" } finally {",
" resetCallerInLocalThread();",
" }",
" }",
" }",
"}",
"");
final String expectedStandardPhysicEnvironment = multilineString(
Expand Down

0 comments on commit 5113a2d

Please sign in to comment.