com.redhat.ceylon.model.loader.JvmBackendUtil Maven / Gradle / Ivy
package com.redhat.ceylon.model.loader;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.getSignature;
import java.util.List;
import java.util.Map;
import com.redhat.ceylon.common.BooleanUtil;
import com.redhat.ceylon.model.loader.mirror.AnnotatedMirror;
import com.redhat.ceylon.model.loader.mirror.AnnotationMirror;
import com.redhat.ceylon.model.loader.mirror.ClassMirror;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassAlias;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
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.Functional;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Specification;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;
public class JvmBackendUtil {
public static boolean isInitialLowerCase(String name) {
return !name.isEmpty() && isLowerCase(name.codePointAt(0));
}
public static boolean isLowerCase(int codepoint) {
return Character.isLowerCase(codepoint) || codepoint == '_';
}
public static String getName(List parts){
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.size(); i++) {
sb.append(parts.get(i));
if (i < parts.size() - 1) {
sb.append('.');
}
}
return sb.toString();
}
public static String getMirrorName(AnnotatedMirror mirror) {
String name;
AnnotationMirror annot = mirror.getAnnotation(AbstractModelLoader.CEYLON_NAME_ANNOTATION);
if (annot != null) {
name = (String)annot.getValue();
} else {
name = mirror.getName();
name = name.isEmpty() ? name : NamingBase.stripLeadingDollar(name);
if (mirror instanceof ClassMirror
&& JvmBackendUtil.isInitialLowerCase(name)
&& name.endsWith("_")
&& mirror.getAnnotation(AbstractModelLoader.CEYLON_CEYLON_ANNOTATION) != null) {
name = name.substring(0, name.length()-1);
}
}
return name;
}
public static boolean isSubPackage(String moduleName, String pkgName) {
return pkgName.equals(moduleName)
|| pkgName.startsWith(moduleName+".");
}
/**
* Removes the given character from the given string. More efficient than using String.replace
* which uses regexes.
*/
public static String removeChar(char c, String string) {
int nextChar = string.indexOf(c);
if(nextChar == -1)
return string;
int start = 0;
StringBuilder ret = new StringBuilder(string.length()-1);// we remove at least one
while(nextChar != -1){
ret.append(string, start, nextChar);
start = nextChar+1;
nextChar = string.indexOf(c, start);
}
// don't forget the end part
ret.append(string, start, string.length());
return ret.toString();
}
public static String strip(String name, boolean isCeylon, boolean isShared) {
String stripped = NamingBase.stripLeadingDollar(name);
String privSuffix = NamingBase.Suffix.$priv$.name();
if(isCeylon && !isShared && name.endsWith(privSuffix))
return stripped.substring(0, stripped.length() - privSuffix.length());
return stripped;
}
/**
* Determines whether the declaration is a non-transient non-parameter value
* (not a getter)
* @param decl The declaration
* @return true if the declaration is a value
*/
public static boolean isValue(Declaration decl) {
return (decl instanceof Value)
&& !((Value)decl).isParameter()
&& !((Value)decl).isTransient();
}
/**
* Determines whether the declaration's is a method
* @param decl The declaration
* @return true if the declaration is a method
*/
public static boolean isMethod(Declaration decl) {
return (decl instanceof Function)
&& !((Function)decl).isParameter();
}
public static boolean isCeylon(TypeDeclaration declaration) {
return ModelUtil.isCeylonDeclaration(declaration);
}
public static Declaration getTopmostRefinedDeclaration(Declaration decl){
return getTopmostRefinedDeclaration(decl, null);
}
public static Declaration getTopmostRefinedDeclaration(Declaration decl, Map methodOverrides){
if (decl instanceof FunctionOrValue
&& ((FunctionOrValue)decl).isParameter()
&& decl.getContainer() instanceof Class) {
// Parameters in a refined class are not considered refinements themselves
// We have in find the refined attribute
Class c = (Class)decl.getContainer();
boolean isAlias = c.isAlias();
boolean isActual = c.isActual();
// aliases and actual classes actually do refine their extended type parameters so the same rules apply wrt
// boxing and stuff
if (isAlias || isActual) {
Functional ctor = null;
int index = c.getParameterList().getParameters().indexOf(findParamForDecl(((TypedDeclaration)decl)));
// ATM we only consider aliases if we're looking at aliases, and same for actual, not mixing the two.
// Note(Stef): not entirely sure about that one, what about aliases of actual classes?
while ((isAlias && c.isAlias())
|| (isActual && c.isActual())) {
ctor = (isAlias && c.isAlias()) ? (Functional)((ClassAlias)c).getConstructor() : c;
Type et = c.getExtendedType();
c = et!=null && et.isClass() ? (Class)et.getDeclaration() : null;
// handle compile errors
if(c == null)
return null;
}
if (isActual) {
ctor = c;
}
// be safe
if(ctor == null
|| ctor.getParameterLists() == null
|| ctor.getParameterLists().isEmpty()
|| ctor.getFirstParameterList() == null
|| ctor.getFirstParameterList().getParameters() == null
|| ctor.getFirstParameterList().getParameters().size() <= index)
return null;
decl = ctor.getFirstParameterList().getParameters().get(index).getModel();
}
if (decl.isShared()) {
Declaration refinedDecl = c.getRefinedMember(decl.getName(), getSignature(decl), false);//?? ellipsis=false??
if(refinedDecl != null && !ModelUtil.equal(refinedDecl, decl)) {
return getTopmostRefinedDeclaration(refinedDecl, methodOverrides);
}
}
return decl;
} else if(decl instanceof FunctionOrValue
&& ((FunctionOrValue)decl).isParameter() // a parameter
&& ((decl.getContainer() instanceof Function && !(((Function)decl.getContainer()).isParameter())) // that's not parameter of a functional parameter
|| decl.getContainer() instanceof Specification // or is a parameter in a specification
|| (decl.getContainer() instanceof Function
&& ((Function)decl.getContainer()).isParameter()
&& createMethod((Function)decl.getContainer())))) {// or is a class functional parameter
// Parameters in a refined method are not considered refinements themselves
// so we have to look up the corresponding parameter in the container's refined declaration
Functional func = (Functional)getParameterized((FunctionOrValue)decl);
if(func == null)
return decl;
Declaration kk = getTopmostRefinedDeclaration((Declaration)func, methodOverrides);
// error recovery
if(kk instanceof Functional == false)
return decl;
Functional refinedFunc = (Functional) kk;
// shortcut if the functional doesn't override anything
if (ModelUtil.equal((Declaration)refinedFunc, (Declaration)func)) {
return decl;
}
if (func.getParameterLists().size() != refinedFunc.getParameterLists().size()) {
// invalid input
return decl;
}
for (int ii = 0; ii < func.getParameterLists().size(); ii++) {
if (func.getParameterLists().get(ii).getParameters().size() != refinedFunc.getParameterLists().get(ii).getParameters().size()) {
// invalid input
return decl;
}
// find the index of the parameter in the declaration
int index = 0;
for (Parameter px : func.getParameterLists().get(ii).getParameters()) {
if (px.getModel() == null || px.getModel().equals(decl)) {
// And return the corresponding parameter from the refined declaration
return refinedFunc.getParameterLists().get(ii).getParameters().get(index).getModel();
}
index++;
}
continue;
}
}else if(methodOverrides != null
&& decl instanceof Function
&& ModelUtil.equal(decl.getRefinedDeclaration(), decl)
&& decl.getContainer() instanceof Specification
&& ((Specification)decl.getContainer()).getDeclaration() instanceof Function
&& ((Function) ((Specification)decl.getContainer()).getDeclaration()).isShortcutRefinement()
// we do all the previous ones first because they are likely to filter out false positives cheaper than the
// hash lookup we do next to make sure it is really one of those cases
&& methodOverrides.containsKey(decl)){
// special case for class X() extends T(){ m = function() => e; } which we inline
decl = methodOverrides.get(decl);
}
Declaration refinedDecl = decl.getRefinedDeclaration();
if(refinedDecl != null && !ModelUtil.equal(refinedDecl, decl))
return getTopmostRefinedDeclaration(refinedDecl);
return decl;
}
public static Parameter findParamForDecl(TypedDeclaration decl) {
String attrName = decl.getName();
return findParamForDecl(attrName, decl);
}
public static Parameter findParamForDecl(String attrName, TypedDeclaration decl) {
Parameter result = null;
if (decl.getContainer() instanceof Functional) {
Functional f = (Functional)decl.getContainer();
result = f.getParameter(attrName);
}
return result;
}
public static Declaration getParameterized(FunctionOrValue methodOrValue) {
if (!methodOrValue.isParameter()) {
return null;
}
Scope scope = methodOrValue.getContainer();
if (scope instanceof Specification) {
return ((Specification)scope).getDeclaration();
} else if (scope instanceof Declaration) {
return (Declaration)scope;
}
return null;
}
public static boolean createMethod(FunctionOrValue model) {
return model instanceof Function
&& model.isParameter()
&& model.isClassMember()
&& (model.isShared() || model.isCaptured());
}
public static boolean supportsReified(Declaration declaration){
if(declaration instanceof ClassOrInterface){
// Java constructors don't support reified type arguments
return isCeylon((TypeDeclaration) declaration);
}else if(declaration instanceof Function){
if (((Function)declaration).isParameter()) {
// those can never be parameterised
return false;
}
if(declaration.isToplevel())
return true;
// Java methods don't support reified type arguments
Function m = (Function) getTopmostRefinedDeclaration(declaration);
// See what its container is
ClassOrInterface container = ModelUtil.getClassOrInterfaceContainer(m);
// a method which is not a toplevel and is not a class method, must be a method within method and
// that must be Ceylon so it supports it
if(container == null)
return true;
return supportsReified(container);
}else if(declaration instanceof Constructor){
// Java constructors don't support reified type arguments
return isCeylon((Constructor) declaration);
}else{
return false;
}
}
public static boolean isCompanionClassNeeded(TypeDeclaration decl) {
return decl instanceof Interface
&& BooleanUtil.isNotFalse(((Interface)decl).isCompanionClassNeeded());
}
/**
* Determines whether the given attribute should be accessed and assigned
* via a {@code VariableBox}
*/
public static boolean isBoxedVariable(TypedDeclaration attr) {
return ModelUtil.isNonTransientValue(attr)
&& ModelUtil.isLocalNotInitializer(attr)
&& ((attr.isVariable() && attr.isCaptured())
// self-captured objects must also be boxed like variables
|| attr.isSelfCaptured());
}
private static String getArrayName(TypeDeclaration decl) {
if(decl instanceof Class == false)
return null;
return decl.getQualifiedNameString();
}
public static boolean isJavaArray(TypeDeclaration decl) {
String name = getArrayName(decl);
return "java.lang::ObjectArray".equals(name)
|| "java.lang::ByteArray".equals(name)
|| "java.lang::ShortArray".equals(name)
|| "java.lang::IntArray".equals(name)
|| "java.lang::LongArray".equals(name)
|| "java.lang::FloatArray".equals(name)
|| "java.lang::DoubleArray".equals(name)
|| "java.lang::BooleanArray".equals(name)
|| "java.lang::CharArray".equals(name);
}
public static boolean isJavaBooleanArray(TypeDeclaration decl) {
return "java.lang::BooleanArray".equals(getArrayName(decl));
}
public static boolean isJavaByteArray(TypeDeclaration decl) {
return "java.lang::ByteArray".equals(getArrayName(decl));
}
public static boolean isJavaShortArray(TypeDeclaration decl) {
return "java.lang::ShortArray".equals(getArrayName(decl));
}
public static boolean isJavaIntArray(TypeDeclaration decl) {
return "java.lang::IntArray".equals(getArrayName(decl));
}
public static boolean isJavaLongArray(TypeDeclaration decl) {
return "java.lang::LongArray".equals(getArrayName(decl));
}
public static boolean isJavaFloatArray(TypeDeclaration decl) {
return "java.lang::FloatArray".equals(getArrayName(decl));
}
public static boolean isJavaDoubleArray(TypeDeclaration decl) {
return "java.lang::DoubleArray".equals(getArrayName(decl));
}
public static boolean isJavaCharArray(TypeDeclaration decl) {
return "java.lang::CharArray".equals(getArrayName(decl));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy