com.redhat.ceylon.compiler.java.codegen.MethodDefinitionBuilder Maven / Gradle / Ivy
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.compiler.java.codegen;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.ABSTRACT;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.FINAL;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.PUBLIC;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.STATIC;
import static com.redhat.ceylon.langtools.tools.javac.code.TypeTags.VOID;
import java.util.Collections;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.langtools.tools.javac.code.Flags;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCAnnotation;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCBlock;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCStatement;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCTypeParameter;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCVariableDecl;
import com.redhat.ceylon.langtools.tools.javac.util.List;
import com.redhat.ceylon.langtools.tools.javac.util.ListBuffer;
import com.redhat.ceylon.langtools.tools.javac.util.Name;
import com.redhat.ceylon.model.typechecker.model.Annotation;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.FunctionOrValue;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.ParameterList;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypedReference;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;
/**
* Builder for Java Methods. With special pre-definied builders
* for normal methods, constructors, getters and setters.
*
* @author Tako Schotanus
*/
public class MethodDefinitionBuilder
implements ParameterizedBuilder {
private final AbstractTransformer gen;
private final String name;
private String realName;
private long modifiers;
private boolean isOverride;
private boolean isAbstract;
private boolean isTransient;
private JCExpression resultTypeExpr;
private List resultTypeAnnos;
private final ListBuffer userAnnotations = ListBuffer.lb();
private final ListBuffer modelAnnotations = ListBuffer.lb();
private final ListBuffer typeParams = ListBuffer.lb();
private final ListBuffer typeParamAnnotations = ListBuffer.lb();
private final ListBuffer params = ListBuffer.lb();
private ListBuffer body = ListBuffer.lb();
private int annotationFlags = Annotations.MODEL_AND_USER;
private boolean built = false;
private JCExpression defaultValue;
private boolean haveLocation = false;
private Node location;
public static MethodDefinitionBuilder method(AbstractTransformer gen, Function method) {
return new MethodDefinitionBuilder(gen, false, gen.naming.selector(method));
}
public static MethodDefinitionBuilder method(AbstractTransformer gen, TypedDeclaration decl, int namingOptions) {
return new MethodDefinitionBuilder(gen, false, Naming.selector(decl, namingOptions));
}
public static MethodDefinitionBuilder getter(AbstractTransformer gen, TypedDeclaration attr, boolean indirect) {
MethodDefinitionBuilder mdb = new MethodDefinitionBuilder(gen, false, Naming.getGetterName(attr, indirect));
if (Naming.isAmbiguousGetterName(attr)) {
mdb.realName(attr.getName());
}
return mdb;
}
public static MethodDefinitionBuilder setter(AbstractTransformer gen, TypedDeclaration attr) {
MethodDefinitionBuilder mdb = new MethodDefinitionBuilder(gen, false, Naming.getSetterName(attr));
if (Naming.isAmbiguousGetterName(attr)) {
mdb.realName(attr.getName());
}
return mdb;
}
public static MethodDefinitionBuilder callable(AbstractTransformer gen) {
return systemMethod(gen, Naming.getCallableMethodName());
}
public static MethodDefinitionBuilder systemMethod(AbstractTransformer gen, String name) {
MethodDefinitionBuilder builder = new MethodDefinitionBuilder(gen, true, name);
return builder;
}
public static MethodDefinitionBuilder constructor(AbstractTransformer gen) {
return new MethodDefinitionBuilder(gen, false, null);
}
public static MethodDefinitionBuilder main(AbstractTransformer gen) {
MethodDefinitionBuilder mdb = new MethodDefinitionBuilder(gen, false, "main")
.modifiers(PUBLIC | STATIC);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(mdb.gen, "args");
pdb.type(gen.make().TypeArray(gen.make().Type(gen.syms().stringType)), List.nil());
return mdb.parameter(pdb);
}
private MethodDefinitionBuilder(AbstractTransformer gen, boolean ignoreAnnotations, String name) {
this.gen = gen;
this.name = name;
if (ignoreAnnotations) {
this.annotationFlags = Annotations.ignore(this.annotationFlags);
}
resultTypeExpr = makeVoidType();
}
public MethodDefinitionBuilder realName(String realName) {
this.realName = realName;
return this;
}
private ListBuffer getAnnotations() {
ListBuffer result = ListBuffer.lb();
if (Annotations.includeUser(this.annotationFlags)) {
result.appendList(this.userAnnotations);
}
if (Annotations.includeModel(this.annotationFlags)) {
result.appendList(this.modelAnnotations);
}
if (isOverride) {
result.appendList(gen.makeAtOverride());
}
if (Annotations.includeIgnore(this.annotationFlags)) {
result.appendList(gen.makeAtIgnore());
}
if (Annotations.includeModel(this.annotationFlags)) {
if (resultTypeAnnos != null) {
result.appendList(resultTypeAnnos);
}
if(!typeParamAnnotations.isEmpty()) {
result.appendList(gen.makeAtTypeParameters(typeParamAnnotations.toList()));
}
if (isTransient) {
result.appendList(gen.makeAtTransient());
}
if(realName != null){
result.appendList(gen.makeAtName(realName));
}
}
return result;
}
public MethodDefinitionBuilder location(Node at) {
this.haveLocation = true;
this.location = at;
return this;
}
public JCTree.JCMethodDecl build() {
if (built) {
throw new BugException("already built");
}
built = true;
if (haveLocation) {
gen.at(location);
}
ListBuffer params = ListBuffer.lb();
for (ParameterDefinitionBuilder pdb : this.params) {
if (!Annotations.includeModel(this.annotationFlags)) {
pdb.noModelAnnotations();
}
params.append(pdb.build());
}
return gen.make().MethodDef(
gen.make().Modifiers(modifiers, getAnnotations().toList()),
makeName(name),
resultTypeExpr,
typeParams.toList(),
params.toList(),
List. nil(),
makeBody(body),
defaultValue);
}
private Name makeName(String name) {
if (name != null) {
return gen.names().fromString(Naming.quoteMethodName(name));
} else {
return gen.names().init;
}
}
private JCBlock makeBody(ListBuffer body) {
for (ParameterDefinitionBuilder pdb : params) {
if (pdb.requiresBoxedVariableDecl()) {
body.prepend(pdb.buildBoxedVariableDecl());
}
}
return (!isAbstract && (body != null) && ((modifiers & ABSTRACT) == 0)) ? gen.make().Block(0, body.toList()) : null;
}
JCExpression makeVoidType() {
return gen.make().TypeIdent(VOID);
}
JCExpression makeResultType(TypedDeclaration typedDeclaration, Type type, int flags) {
if (typedDeclaration == null
|| ((!(typedDeclaration instanceof Function) || !((Function)typedDeclaration).isParameter())
&& AbstractTransformer.isAnything(type))) {
if ((typedDeclaration instanceof Function)
&& ((Function)typedDeclaration).isDeclaredVoid()
&& !Strategy.useBoxedVoid((Function)typedDeclaration)) {
return makeVoidType();
} else {
return gen.makeJavaType(typedDeclaration, gen.typeFact().getAnythingType(), flags);
}
} else {
return gen.makeJavaType(typedDeclaration, type, flags);
}
}
/*
* Builder methods - they transform the inner state before doing the final construction
*/
public MethodDefinitionBuilder modifiers(long... modifiers) {
long mods = 0;
for (long mod : modifiers) {
mods |= mod;
}
this.modifiers = mods;
return this;
}
/**
* {@code @Ignore} and no model annotations.
*/
public MethodDefinitionBuilder ignoreModelAnnotations() {
this.annotationFlags = Annotations.ignore(annotationFlags);
return this;
}
/** No model annotations, but possibly still {@link @Ignore} and user annotations. */
public MethodDefinitionBuilder noModelAnnotations() {
this.annotationFlags = Annotations.noModel(annotationFlags);
return this;
}
/** No annotations at all (including {@code @Ignore}). */
public MethodDefinitionBuilder noAnnotations() {
this.annotationFlags = 0;
return this;
}
public MethodDefinitionBuilder annotationFlags(int annotationFlags) {
this.annotationFlags = annotationFlags;
return this;
}
public MethodDefinitionBuilder modelAnnotations(List modelAnnotations) {
this.modelAnnotations.appendList(modelAnnotations);
return this;
}
public MethodDefinitionBuilder userAnnotations(List annotations) {
this.userAnnotations.appendList(annotations);
return this;
}
public MethodDefinitionBuilder typeParameter(TypeParameter param, java.util.List producedBounds) {
return typeParameter(gen.makeTypeParameter(param, producedBounds), gen.makeAtTypeParameter(param));
}
public MethodDefinitionBuilder typeParameter(TypeParameter param) {
return typeParameter(param, null);
}
private MethodDefinitionBuilder typeParameter(JCTypeParameter tp, JCAnnotation tpAnno) {
typeParams.append(tp);
typeParamAnnotations.append(tpAnno);
return this;
}
public MethodDefinitionBuilder parameters(List pdbs) {
params.appendList(pdbs);
return this;
}
public MethodDefinitionBuilder parameter(ParameterDefinitionBuilder pdb) {
params.append(pdb);
return this;
}
public MethodDefinitionBuilder parameter(long modifiers,
java.util.List annos,
String name,
Parameter decl,
TypedDeclaration nonWideningDecl,
Type nonWideningType,
int flags, boolean canWiden) {
return parameter(modifiers, annos, null, name, name, decl, nonWideningDecl, nonWideningType, flags);
}
private MethodDefinitionBuilder parameter(long modifiers,
java.util.List modelAnnotations, List userAnnotations,
String name, String aliasedName,
Parameter decl, TypedDeclaration nonWideningDecl, Type nonWideningType,
int flags) {
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.explicitParameter(gen, decl);
pdb.modifiers(modifiers);
pdb.modelAnnotations(modelAnnotations);
pdb.userAnnotations(userAnnotations);
pdb.aliasName(aliasedName);
pdb.sequenced(decl.isSequenced());
pdb.defaulted(decl.isDefaulted());
if (isParamTypeLocalToMethod(decl,
nonWideningType)) {
pdb.type(gen.make().Type(gen.syms().objectType), gen.makeJavaTypeAnnotations(decl.getModel()));
} else {
pdb.type(paramType(gen, nonWideningDecl, nonWideningType, flags), gen.makeJavaTypeAnnotations(decl.getModel()));
}
return parameter(pdb);
}
private boolean isParamTypeLocalToMethod(Parameter parameter,
Type nonWideningType) {
// error recovery
if(nonWideningType == null)
return false;
if (parameter.getModel().getTypeErased()) {
return false;
}
Declaration method = parameter.getDeclaration();
TypeDeclaration paramTypeDecl = nonWideningType.getDeclaration();
if (paramTypeDecl instanceof TypeParameter
&& Decl.equalScopeDecl(paramTypeDecl.getContainer(), method)) {
return false;
}
Scope scope = paramTypeDecl.getContainer();
while (scope != null && !(scope instanceof Package)) {
if (Decl.equalScopeDecl(scope, method)) {
return true;
}
scope = scope.getContainer();
}
return false;
}
static JCExpression paramType(AbstractTransformer gen, TypedDeclaration nonWideningDecl,
Type nonWideningType, int flags) {
return gen.makeJavaType(nonWideningDecl, nonWideningType, flags);
}
public MethodDefinitionBuilder parameter(Parameter paramDecl,
Type paramType, int mods, int flags, boolean canWiden) {
String name = paramDecl.getName();
return parameter(mods, paramDecl.getModel().getAnnotations(),
name, paramDecl, paramDecl.getModel(), paramType, flags, canWiden);
}
static class NonWideningParam {
public final int flags;
public final Type nonWideningType;
public final TypedDeclaration nonWideningDecl;
NonWideningParam(int mods, Type nonWideningType, TypedDeclaration nonWideningDecl){
this.flags = mods;
this.nonWideningType = nonWideningType;
this.nonWideningDecl = nonWideningDecl;
}
}
enum WideningRules {
// Stef: let me be very clear that I've no idea what those two are for
NONE, CAN_WIDEN,
// this is so we get widening code enabled for mixin bridge methods
FOR_MIXIN;
}
public MethodDefinitionBuilder parameter(Parameter param,
List userAnnotations, int flags, WideningRules wideningRules) {
return parameter(param, null, userAnnotations, flags, wideningRules);
}
public MethodDefinitionBuilder parameter(Parameter param, TypedReference typedRef,
List userAnnotations, int flags, WideningRules wideningRules) {
String paramName = param.getName();
String aliasedName = Naming.getAliasedParameterName(param);
FunctionOrValue mov = CodegenUtil.findMethodOrValueForParam(param);
if(typedRef == null)
typedRef = gen.getTypedReference(mov);
int mods = 0;
if (!Decl.isNonTransientValue(mov) || !mov.isVariable() || mov.isCaptured()) {
mods |= FINAL;
}
NonWideningParam nonWideningParam = getNonWideningParam(typedRef, wideningRules);
flags |= nonWideningParam.flags;
return parameter(mods, param.getModel().getAnnotations(), userAnnotations, paramName, aliasedName, param,
nonWideningParam.nonWideningDecl, nonWideningParam.nonWideningType, flags);
}
public NonWideningParam getNonWideningParam(FunctionOrValue mov,
WideningRules wideningRules) {
return getNonWideningParam(gen.getTypedReference(mov), wideningRules);
}
public NonWideningParam getNonWideningParam(TypedReference typedRef,
WideningRules wideningRules) {
TypedDeclaration nonWideningDecl = null;
int flags = 0;
Type nonWideningType;
FunctionOrValue mov = (FunctionOrValue) typedRef.getDeclaration();
if (Decl.isValue(mov)) {
TypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
nonWideningDecl = nonWideningTypedRef.getDeclaration();
}else{
// Stef: So here's the thing. I know this is wrong for Function where we should do getFullType(), BUT
// lots of methods call this and then feed the output into AT.makeJavaType(TypedDeclaration typeDecl, Type type, int flags)
// which adds the Callable type, so if we fix it here we have to remove it from there and there's lots of callers of that
// function which rely on its behaviour and frankly I've had enough of this refactoring, so a few callers of this function
// have to add the Callable back. It sucks, yeah, but so far it works, which is amazing enough that I don't want to touch it
// any more. More ambitious/courageous people are welcome to fix this properly.
nonWideningType = typedRef.getType();
nonWideningDecl = mov;
}
if(!CodegenUtil.isUnBoxed(nonWideningDecl))
flags |= AbstractTransformer.JT_NO_PRIMITIVES;
// make sure we don't accidentally narrow value parameters that would be erased in the topmost declaration
if(wideningRules != WideningRules.NONE
&& mov instanceof Value){
TypedDeclaration refinedParameter = (TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(mov);
// mixin bridge methods have the same rules as when refining stuff except they are their own refined decl
if(wideningRules == WideningRules.FOR_MIXIN || !Decl.equal(refinedParameter, mov)){
Type refinedParameterType;
// we don't have to use produced typed references with type params applied here because we want to know the
// erasure status of the compilation of the refined parameter, so it's OK if we end up with unbound type parameters
// in the refined parameter type
if(refinedParameter instanceof Function)
refinedParameterType = refinedParameter.appliedTypedReference(null, Collections.emptyList()).getFullType();
else
refinedParameterType = refinedParameter.getType();
// if the supertype method itself got erased to Object, we can't do better than this
if(gen.willEraseToObject(refinedParameterType) && !gen.willEraseToBestBounds(mov))
nonWideningType = gen.typeFact().getObjectType();
else if (CodegenUtil.isRaw(refinedParameter)) {
flags |= AbstractTransformer.JT_RAW;
} else {
flags |= AbstractTransformer.JT_NARROWED;
}
}
}
// keep in sync with gen.willEraseToBestBounds()
if (wideningRules != WideningRules.NONE
&& (gen.typeFact().isUnion(nonWideningType)
|| gen.typeFact().isIntersection(nonWideningType))) {
final Type refinedType = ((TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(nonWideningDecl)).getType();
if (refinedType.isTypeParameter()
&& !refinedType.getSatisfiedTypes().isEmpty()) {
nonWideningType = refinedType.getSatisfiedTypes().get(0);
// Could be parameterized, and type param won't be in scope, so have to go raw
flags |= AbstractTransformer.JT_RAW;
}
}
// this is to be done on the parameter's containing method, to see if that method must have raw parameters
if (mov.isParameter()
&& mov.getContainer() instanceof Declaration
&& gen.rawParameters((Declaration) mov.getContainer())) {
flags |= AbstractTransformer.JT_RAW;
}
return new NonWideningParam(flags, nonWideningType, nonWideningDecl);
}
public MethodDefinitionBuilder isOverride(boolean isOverride) {
this.isOverride = isOverride;
return this;
}
public MethodDefinitionBuilder isAbstract(boolean isAbstract) {
this.isAbstract = isAbstract;
return this;
}
public MethodDefinitionBuilder isTransient(boolean trans) {
this.isTransient = trans;
return this;
}
public MethodDefinitionBuilder body(JCStatement statement) {
if (statement != null) {
this.body.append(statement);
}
return this;
}
public MethodDefinitionBuilder body(List body) {
if (body != null) {
this.body.appendList(body);
}
return this;
}
MethodDefinitionBuilder noBody() {
this.body = null;
return this;
}
public MethodDefinitionBuilder block(JCBlock block) {
if (block != null) {
body.clear();
return body(block.getStatements());
} else {
return noBody();
}
}
public MethodDefinitionBuilder resultType(Function method, int flags) {
if (method.isParameter()) {
if (Decl.isUnboxedVoid(method) && !Strategy.useBoxedVoid(method)) {
return resultType(gen.makeJavaTypeAnnotations(method, false), gen.make().Type(gen.syms().voidType));
} else {
Parameter parameter = method.getInitializerParameter();
Type resultType = parameter.getType();
for (int ii = 1; ii < method.getParameterLists().size(); ii++) {
resultType = gen.typeFact().getCallableType(resultType);
}
return resultType(gen.makeJavaType(resultType, CodegenUtil.isUnBoxed(method) ? 0 : AbstractTransformer.JT_NO_PRIMITIVES), method);
}
}
TypedReference typedRef = gen.getTypedReference(method);
TypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
Type nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
if(method.isActual()
&& CodegenUtil.hasTypeErased(method))
flags |= AbstractTransformer.JT_RAW;
return resultType(makeResultType(nonWideningTypedRef.getDeclaration(), nonWideningType, flags), method);
}
public MethodDefinitionBuilder resultTypeNonWidening(Type currentType, TypedReference typedRef,
Type returnType, int flags){
TypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef, currentType);
returnType = gen.nonWideningType(typedRef, nonWideningTypedRef);
return resultType(makeResultType(nonWideningTypedRef.getDeclaration(), returnType, flags), typedRef.getDeclaration());
}
public MethodDefinitionBuilder resultType(TypedDeclaration resultType, Type type, int flags) {
return resultType(makeResultType(resultType, type, flags), resultType);
}
public MethodDefinitionBuilder resultType(JCExpression resultType, TypedDeclaration typeDecl) {
return resultType(gen.makeJavaTypeAnnotations(typeDecl, false), resultType);
}
public MethodDefinitionBuilder resultType(List resultTypeAnnos, JCExpression resultType) {
this.resultTypeAnnos = resultTypeAnnos;
this.resultTypeExpr = resultType;
return this;
}
public MethodDefinitionBuilder modelAnnotations(java.util.List annotations) {
modelAnnotations(gen.makeAtAnnotations(annotations));
return this;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getAnnotations()).append(' ');
sb.append(Flags.toString(this.modifiers)).append(' ');
sb.append(resultTypeExpr).append(' ');
sb.append(name).append('(');
int i = 0;
for (ParameterDefinitionBuilder param : params) {
sb.append(param);
if (i < params.count -1) {
sb.append(',');
}
}
sb.append(')');
return sb.toString();
}
public MethodDefinitionBuilder reifiedTypeParameters(java.util.List typeParams) {
for(TypeParameter typeParam : typeParams)
reifiedTypeParameter(typeParam);
return this;
}
public MethodDefinitionBuilder reifiedTypeParameter(TypeParameter param) {
String descriptorName = gen.naming.getTypeArgumentDescriptorName(param);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.implicitParameter(gen, descriptorName);
pdb.type(gen.makeTypeDescriptorType(), List.nil());
pdb.modifiers(FINAL);
if(!Annotations.includeModel(this.annotationFlags))
pdb.noUserOrModelAnnotations();
else
pdb.ignored();
parameter(pdb);
return this;
}
public MethodDefinitionBuilder reifiedTypeParametersFromModel(java.util.List typeParameters) {
for(TypeParameter typeParam : typeParameters)
reifiedTypeParameter(typeParam);
return this;
}
public void defaultValue(JCExpression defaultValue) {
this.defaultValue = defaultValue;
}
public void mpl(java.util.List parameterLists) {
StringBuilder sb = new StringBuilder();
for (int ii = 1; ii < parameterLists.size(); ii++) {
ParameterList parameterList = parameterLists.get(ii);
ParameterDefinitionBuilder.functionalParameters(sb, parameterList);
}
modelAnnotations(gen.makeAtFunctionalParameter(sb.toString()));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy