Skip to content

Commit

Permalink
Merge pull request #2898 from LorenzoBettini/temp-xtend-anonymous-cla…
Browse files Browse the repository at this point in the history
…ss-2

Xtend refactoring handling of anonymous class
  • Loading branch information
LorenzoBettini authored Feb 6, 2024
2 parents 88c9ed9 + 6af742c commit 447dc2c
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class JvmModelTests extends AbstractXtendTestCase {
assertTrue(anonymous.final)
assertFalse(anonymous.static)
assertTrue(anonymous.local)
assertFalse(anonymous.anonymous) // additional member -> named local class
assertTrue(anonymous.anonymous)
assertEquals(JvmVisibility.DEFAULT, anonymous.visibility)
assertEquals(2, anonymous.superTypes.size)
assertEquals('java.lang.Runnable', anonymous.superTypes.last.qualifiedName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ public void testAnonymousClass_01() {
Assert.assertTrue(anonymous.isFinal());
Assert.assertFalse(anonymous.isStatic());
Assert.assertTrue(anonymous.isLocal());
Assert.assertFalse(anonymous.isAnonymous());
Assert.assertTrue(anonymous.isAnonymous());
Assert.assertEquals(JvmVisibility.DEFAULT, anonymous.getVisibility());
Assert.assertEquals(2, anonymous.getSuperTypes().size());
Assert.assertEquals("java.lang.Runnable", IterableExtensions.<JvmTypeReference>last(anonymous.getSuperTypes()).getQualifiedName());
Expand Down
1 change: 1 addition & 0 deletions org.eclipse.xtend.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-11
Export-Package: org.eclipse.xtend.core;version="2.34.0";x-friends:="org.eclipse.xtend.ide.common,org.eclipse.xtend.ide.tests,org.eclipse.xtend.core.tests",
org.eclipse.xtend.core.compiler;version="2.34.0";x-friends:="org.eclipse.xtend.m2e,org.eclipse.xtend.ide.tests,org.eclipse.xtend.core.tests",
org.eclipse.xtend.core.compiler.batch;version="2.34.0",
org.eclipse.xtend.core.compiler.output;version="2.34.0";x-internal:=true,
org.eclipse.xtend.core.conversion;version="2.34.0";x-friends:="org.eclipse.xtend.ide,org.eclipse.xtend.ide.common,org.eclipse.xtend.core.tests",
org.eclipse.xtend.core.documentation;version="2.34.0";x-internal:=true,
org.eclipse.xtend.core.findReferences;version="2.34.0";x-internal:=true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*******************************************************************************
* Copyright (c) 2023 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtend.core.compiler;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend.core.xtend.AnonymousClass;
import org.eclipse.xtend.core.xtend.XtendField;
import org.eclipse.xtend.core.xtend.XtendFunction;
import org.eclipse.xtend.core.xtend.XtendMember;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.util.IResourceScopeCache;
import org.eclipse.xtext.util.Tuples;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;

import com.google.inject.Inject;

/**
* @author Lorenzo Bettini - Initial contribution and API
*/
public class AnonymousClassCompilerHelper {

@Inject
private IResourceScopeCache cache;

@Inject
private IJvmModelAssociations associations;

/**
* Assumes that the passed type is anonymous.
*/
public boolean canCompileToJavaAnonymousClass(JvmType type) {
return cache.get(Tuples.pair("anonymousJava", type), type.eResource(), () -> {
EObject sourceElement = associations.getPrimarySourceElement(type);
return sourceElement instanceof AnonymousClass &&
canCompileToJavaAnonymousClass((AnonymousClass) sourceElement);
});
}

public boolean canCompileToJavaAnonymousClass(AnonymousClass anonymousClass) {
return cache.get(Tuples.pair("anonymousJava", anonymousClass), anonymousClass.eResource(), () -> {
for(XtendMember member: anonymousClass.getMembers()) {
if(member instanceof XtendField ||
(member instanceof XtendFunction && !((XtendFunction) member).isOverride()))
return false;
}
return true;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend.core.jvmmodel.IXtendJvmAssociations;
import org.eclipse.xtend.core.richstring.AbstractRichStringPartAcceptor;
import org.eclipse.xtend.core.richstring.DefaultIndentationHandler;
import org.eclipse.xtend.core.richstring.RichStringProcessor;
Expand All @@ -34,7 +33,6 @@
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmField;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeReference;
Expand Down Expand Up @@ -85,7 +83,7 @@ public class XtendCompiler extends XbaseCompiler {
private IGeneratorConfigProvider generatorConfigProvider;

@Inject
private IXtendJvmAssociations associations;
private AnonymousClassCompilerHelper compilerHelper;

@Override
protected String getFavoriteVariableName(EObject ex) {
Expand Down Expand Up @@ -546,9 +544,7 @@ protected void _toJavaStatement(final AnonymousClass anonymousClass, ITreeAppend
@Override
protected boolean internalCanCompileToJavaExpression(XExpression expression, ITreeAppendable appendable) {
if(expression instanceof AnonymousClass) {
AnonymousClass anonymousClass = (AnonymousClass) expression;
JvmGenericType inferredLocalClass = associations.getInferredType(anonymousClass);
return inferredLocalClass.isAnonymous();
return compilerHelper.canCompileToJavaAnonymousClass((AnonymousClass) expression);
} else
return super.internalCanCompileToJavaExpression(expression, appendable);
}
Expand Down Expand Up @@ -611,5 +607,10 @@ protected boolean needSyntheticSelfVariable(XClosure closure, LightweightTypeRef
}
return false;
}


@Override
protected boolean canCompileToJavaAnonymousClass(XConstructorCall constructorCall) {
return super.canCompileToJavaAnonymousClass(constructorCall) &&
compilerHelper.canCompileToJavaAnonymousClass((AnonymousClass) constructorCall.eContainer());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable
import org.eclipse.xtext.xbase.compiler.output.SharedAppendableState
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver
import org.eclipse.xtext.xbase.typesystem.references.ITypeReferenceOwner
import org.eclipse.xtext.xbase.compiler.ImportManager
import org.eclipse.xtext.generator.trace.ITraceURIConverter
import org.eclipse.xtext.resource.ILocationInFileProvider
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtend.core.compiler.output.AnonymousClassAwareTreeAppendable

/**
* @author Sven Efftinge - Initial contribution and API
Expand All @@ -56,7 +62,8 @@ class XtendGenerator extends JvmModelGenerator implements IGenerator2 {
@Inject OperationCanceledManager operationCanceledManager

@Inject ElementIssueProvider.Factory issueProviderFactory

@Inject AnonymousClassCompilerHelper compilerHelper

override doGenerate(Resource input, IFileSystemAccess fsa) {
super.doGenerate(input, fsa)
callMacroProcessors(input)
Expand Down Expand Up @@ -164,9 +171,12 @@ class XtendGenerator extends JvmModelGenerator implements IGenerator2 {
}

def compileLocalTypeStubs(JvmFeature feature, ITreeAppendable appendable, GeneratorConfig config) {
feature.localClasses.filter[ !anonymous ].forEach[
appendable.newLine
feature.localClasses.forEach[
if (compilerHelper.canCompileToJavaAnonymousClass(it)) {
return
}
val anonymousClass = sourceElements.head as AnonymousClass
appendable.newLine
val childAppendable = appendable.trace(anonymousClass)
childAppendable.append('abstract class ')
childAppendable.traceSignificant(anonymousClass).append(simpleName)
Expand Down Expand Up @@ -320,7 +330,7 @@ class XtendGenerator extends JvmModelGenerator implements IGenerator2 {
}
if (declaringType.local && it instanceof JvmOperation) {
val declarator = declaringType as JvmGenericType
if (!declarator.anonymous) {
if (!compilerHelper.canCompileToJavaAnonymousClass(declarator)) {
return result
}
}
Expand Down Expand Up @@ -386,5 +396,9 @@ class XtendGenerator extends JvmModelGenerator implements IGenerator2 {
}

}


override createAppendable(ImportManager importManager, ITraceURIConverter converter, ILocationInFileProvider locationProvider, IJvmModelAssociations jvmModelAssociations, EObject source, String indentation, String lineSeparator) {
return new AnonymousClassAwareTreeAppendable(compilerHelper, importManager, converter, locationProvider, jvmModelAssociations, source, indentation, lineSeparator)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2023 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtend.core.compiler.output;

import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend.core.compiler.AnonymousClassCompilerHelper;
import org.eclipse.xtext.generator.trace.ILocationData;
import org.eclipse.xtext.generator.trace.ITraceURIConverter;
import org.eclipse.xtext.resource.ILocationInFileProvider;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.compiler.output.SharedAppendableState;
import org.eclipse.xtext.xbase.compiler.output.TreeAppendable;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceSerializer;
import org.eclipse.xtext.xbase.typesystem.references.ParameterizedTypeReference;

/**
* A custom implementation that takes into consideration anonymous classes,
* which, in some cases, cannot be compiled into standard Java anonymous classes:
* they are compiled into nested local classes.
*
* It uses a custom {@link LightweightTypeReferenceSerializer}.
*
* @author Lorenzo Bettini - Initial contribution and API
*/
public class AnonymousClassAwareTreeAppendable extends TreeAppendable {

private AnonymousClassCompilerHelper compilerHelper;

public AnonymousClassAwareTreeAppendable(AnonymousClassCompilerHelper compilerHelper, ImportManager importManager, ITraceURIConverter converter,
ILocationInFileProvider locationProvider, IJvmModelAssociations jvmModelAssociations, EObject source, String indentation,
String lineSeparator) {
super(importManager, converter, locationProvider, jvmModelAssociations, source, indentation, lineSeparator);
this.compilerHelper = compilerHelper;
}

protected AnonymousClassAwareTreeAppendable(AnonymousClassCompilerHelper compilerHelper, SharedAppendableState state, ITraceURIConverter converter,
ILocationInFileProvider locationProvider, IJvmModelAssociations jvmModelAssociations, Set<ILocationData> sourceLocations,
boolean useForDebugging) {
super(state, converter, locationProvider, jvmModelAssociations, sourceLocations, useForDebugging);
this.compilerHelper = compilerHelper;
}

@Override
protected LightweightTypeReferenceSerializer createLightweightTypeReferenceSerializer() {
return new LightweightTypeReferenceSerializer(this) {
@Override
protected void doVisitParameterizedTypeReference(ParameterizedTypeReference reference) {
if (reference.isAnonymous() &&
!compilerHelper.canCompileToJavaAnonymousClass(reference.getType())) {
// serialize the type of the generated nested local class
AnonymousClassAwareTreeAppendable.this.append(reference.getType());
} else {
super.doVisitParameterizedTypeReference(reference);
}
}
};
}

@Override
protected TreeAppendable createChild(SharedAppendableState state, ITraceURIConverter converter, ILocationInFileProvider locationProvider,
IJvmModelAssociations jvmModelAssociations, Set<ILocationData> newData, boolean useForDebugging) {
return new AnonymousClassAwareTreeAppendable(compilerHelper, state, converter, locationProvider, jvmModelAssociations, newData, useForDebugging);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ public void inferLocalClass(
JvmFeature container) {
final JvmGenericType inferredType = typesFactory.createJvmGenericType();
inferredType.setSimpleName(localClassName);
inferredType.setAnonymous(!hasAdditionalMembers(anonymousClass));
inferredType.setAnonymous(true);
inferredType.setFinal(true);
inferredType.setVisibility(JvmVisibility.DEFAULT);
inferredType.getSuperTypes().add(jvmTypesBuilder.inferredType(anonymousClass));
Expand All @@ -873,14 +873,5 @@ public void inferLocalClass(
associator.associateLogicalContainer(actualParameter, container);
}
}

protected boolean hasAdditionalMembers(AnonymousClass anonymousClass) {
for(XtendMember member: anonymousClass.getMembers()) {
if(member instanceof XtendField ||
(member instanceof XtendFunction && !((XtendFunction) member).isOverride()))
return true;
}
return false;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtend.core.compiler.output.AnonymousClassAwareTreeAppendable;
import org.eclipse.xtend.core.macro.ActiveAnnotationContext;
import org.eclipse.xtend.core.macro.ActiveAnnotationContexts;
import org.eclipse.xtend.core.macro.CodeGenerationContextImpl;
Expand Down Expand Up @@ -48,18 +49,23 @@
import org.eclipse.xtext.generator.IFileSystemAccess2;
import org.eclipse.xtext.generator.IGenerator2;
import org.eclipse.xtext.generator.IGeneratorContext;
import org.eclipse.xtext.generator.trace.ITraceURIConverter;
import org.eclipse.xtext.resource.ILocationInFileProvider;
import org.eclipse.xtext.service.OperationCanceledManager;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.compiler.ElementIssueProvider;
import org.eclipse.xtext.xbase.compiler.GeneratorConfig;
import org.eclipse.xtext.xbase.compiler.ImportManager;
import org.eclipse.xtext.xbase.compiler.JvmModelGenerator;
import org.eclipse.xtext.xbase.compiler.LoopParams;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.eclipse.xtext.xbase.compiler.output.ImportingStringConcatenation;
import org.eclipse.xtext.xbase.compiler.output.SharedAppendableState;
import org.eclipse.xtext.xbase.compiler.output.TreeAppendable;
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
Expand Down Expand Up @@ -91,6 +97,9 @@ private static class StopCollecting extends Exception {
@Inject
private ElementIssueProvider.Factory issueProviderFactory;

@Inject
private AnonymousClassCompilerHelper compilerHelper;

@Override
public void doGenerate(final Resource input, final IFileSystemAccess fsa) {
super.doGenerate(input, fsa);
Expand Down Expand Up @@ -251,14 +260,14 @@ public String reassignThisType(final ITreeAppendable b, final JvmDeclaredType de
}

public void compileLocalTypeStubs(final JvmFeature feature, final ITreeAppendable appendable, final GeneratorConfig config) {
final Function1<JvmGenericType, Boolean> _function = (JvmGenericType it) -> {
boolean _isAnonymous = it.isAnonymous();
return Boolean.valueOf((!_isAnonymous));
};
final Consumer<JvmGenericType> _function_1 = (JvmGenericType it) -> {
appendable.newLine();
final Consumer<JvmGenericType> _function = (JvmGenericType it) -> {
boolean _canCompileToJavaAnonymousClass = this.compilerHelper.canCompileToJavaAnonymousClass(it);
if (_canCompileToJavaAnonymousClass) {
return;
}
EObject _head = IterableExtensions.<EObject>head(this.getSourceElements(it));
final AnonymousClass anonymousClass = ((AnonymousClass) _head);
appendable.newLine();
final ITreeAppendable childAppendable = appendable.trace(anonymousClass);
childAppendable.append("abstract class ");
this._treeAppendableUtil.traceSignificant(childAppendable, anonymousClass).append(it.getSimpleName());
Expand All @@ -277,10 +286,10 @@ public void compileLocalTypeStubs(final JvmFeature feature, final ITreeAppendabl
childAppendable.newLine().append("final ").append(it.getSimpleName()).append(" ").append(thisName).append(" = this;");
childAppendable.blankLine();
}
final Procedure1<LoopParams> _function_2 = (LoopParams it_1) -> {
final Procedure1<LoopParams> _function_1 = (LoopParams it_1) -> {
it_1.setSeparator(this.memberSeparator());
};
final Procedure1<JvmMember> _function_3 = (JvmMember it_1) -> {
final Procedure1<JvmMember> _function_2 = (JvmMember it_1) -> {
final ITreeAppendable memberAppendable = this._treeAppendableUtil.traceWithComments(childAppendable, it_1);
memberAppendable.openScope();
if ((it_1 instanceof JvmOperation)) {
Expand Down Expand Up @@ -345,11 +354,11 @@ public void compileLocalTypeStubs(final JvmFeature feature, final ITreeAppendabl
}
memberAppendable.closeScope();
};
this._loopExtensions.<JvmMember>forEach(childAppendable, this.getAddedDeclarations(it, anonymousClass), _function_2, _function_3);
this._loopExtensions.<JvmMember>forEach(childAppendable, this.getAddedDeclarations(it, anonymousClass), _function_1, _function_2);
childAppendable.decreaseIndentation().newLine().append("}");
appendable.blankLine();
};
IterableExtensions.<JvmGenericType>filter(feature.getLocalClasses(), _function).forEach(_function_1);
feature.getLocalClasses().forEach(_function);
}

private ITreeAppendable generateJavaConstant(final Object value, final ITreeAppendable appendable) {
Expand Down Expand Up @@ -472,8 +481,8 @@ public ITreeAppendable generateVisibilityModifier(final JvmMember it, final ITre
if ((it.getDeclaringType().isLocal() && (it instanceof JvmOperation))) {
JvmDeclaredType _declaringType_1 = it.getDeclaringType();
final JvmGenericType declarator = ((JvmGenericType) _declaringType_1);
boolean _isAnonymous = declarator.isAnonymous();
boolean _not = (!_isAnonymous);
boolean _canCompileToJavaAnonymousClass = this.compilerHelper.canCompileToJavaAnonymousClass(declarator);
boolean _not = (!_canCompileToJavaAnonymousClass);
if (_not) {
return result;
}
Expand Down Expand Up @@ -574,4 +583,9 @@ public ITreeAppendable generateMembersInBody(final JvmDeclaredType it, final ITr
}
return _xifexpression;
}

@Override
public TreeAppendable createAppendable(final ImportManager importManager, final ITraceURIConverter converter, final ILocationInFileProvider locationProvider, final IJvmModelAssociations jvmModelAssociations, final EObject source, final String indentation, final String lineSeparator) {
return new AnonymousClassAwareTreeAppendable(this.compilerHelper, importManager, converter, locationProvider, jvmModelAssociations, source, indentation, lineSeparator);
}
}
Loading

0 comments on commit 447dc2c

Please sign in to comment.