return primaryExpr;
}
}
private JCExpression transformMemberExpression(Tree.StaticMemberOrTypeExpression expr, JCExpression primaryExpr, TermTransformer transformer) {
JCExpression result = null;
// do not throw, an error will already have been reported
Declaration decl = expr.getDeclaration();
if (decl == null) {
return makeErroneous(expr, "compiler bug: expression with no declaration");
}
// Try to find the original declaration, in case we have conditionals that refine the type of objects without us
// creating a tmp variable (in which case we have a substitution for it)
while(decl instanceof TypedDeclaration){
TypedDeclaration typedDecl = (TypedDeclaration) decl;
if(!naming.isSubstituted(decl) && typedDecl.getOriginalDeclaration() != null){
decl = ((TypedDeclaration) decl).getOriginalDeclaration();
}else{
break;
}
}
// Explanation: primaryExpr and qualExpr both specify what is to come before the selector
// but the important difference is that primaryExpr is used for those situations where
// the result comes from the actual Ceylon code while qualExpr is used for those situations
// where we need to refer to synthetic objects (like wrapper classes for toplevel methods)
JCExpression qualExpr = null;
String selector = null;
// true for Java interop using fields, and for super constructor parameters, which must use
// parameters rather than getter methods
boolean mustUseField = false;
// true for default parameter methods
boolean mustUseParameter = false;
if (decl instanceof Functional
&& (!(decl instanceof Method) || !decl.isParameter()
|| functionalParameterRequiresCallable((Method)decl, expr))
&& isFunctionalResult(expr.getTypeModel())) {
result = transformFunctional(expr, (Functional)decl);
} else if (Decl.isGetter(decl)) {
// invoke the getter
if (decl.isToplevel()) {
primaryExpr = null;
qualExpr = naming.makeName((Value)decl, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER);
selector = null;
} else if (Decl.withinClassOrInterface(decl) && !Decl.isLocalToInitializer(decl)) {
selector = naming.selector((Value)decl);
} else {
// method local attr
if (!isRecursiveReference(expr)) {
primaryExpr = naming.makeQualifiedName(primaryExpr, (Value)decl, Naming.NA_Q_LOCAL_INSTANCE);
}
selector = naming.selector((Value)decl);
}
} else if (Decl.isValueOrSharedOrCapturedParam(decl)) {
if (decl.isToplevel()) {
// ERASURE
if ("null".equals(decl.getName())) {
// FIXME this is a pretty brain-dead way to go about erase I think
result = makeNull();
} else if (isBooleanTrue(decl)) {
result = makeBoolean(true);
} else if (isBooleanFalse(decl)) {
result = makeBoolean(false);
} else {
// it's a toplevel attribute
primaryExpr = naming.makeName((TypedDeclaration)decl, Naming.NA_FQ | Naming.NA_WRAPPER);
selector = naming.selector((TypedDeclaration)decl);
}
} else if (Decl.isClassAttribute(decl) || Decl.isClassParameter(decl)) {
mustUseField = Decl.isJavaField(decl)
|| (isWithinSuperInvocation()
&& primaryExpr == null
&& withinSuperInvocation == decl.getContainer());
mustUseParameter = (primaryExpr == null && isWithinDefaultParameterExpression(decl.getContainer()));
if (mustUseField || mustUseParameter){
if(decl instanceof FieldValue) {
selector = ((FieldValue)decl).getRealName();
} else if (isWithinSuperInvocation()
&& ((Value)decl).isVariable()
&& ((Value)decl).isCaptured()) {
selector = Naming.getAliasedParameterName(((Value)decl).getInitializerParameter());
} else {
selector = decl.getName();
}
} else {
// invoke the getter, using the Java interop form of Util.getGetterName because this is the only case
// (Value inside a Class) where we might refer to JavaBean properties
selector = naming.selector((TypedDeclaration)decl);
}
} else if (decl.isCaptured() || decl.isShared()) {
TypedDeclaration typedDecl = ((TypedDeclaration)decl);
TypeDeclaration typeDecl = typedDecl.getType().getDeclaration();
mustUseField = Decl.isBoxedVariable((TypedDeclaration)decl);
if (Decl.isLocalNotInitializer(typeDecl)
&& typeDecl.isAnonymous()
// we need the box if it's a captured object
&& !typedDecl.isSelfCaptured()) {
// accessing a local 'object' declaration, so don't need a getter
} else if (decl.isCaptured()
&& !((TypedDeclaration) decl).isVariable()
// captured objects are never variable but need the box
&& !typedDecl.isSelfCaptured()) {
// accessing a local that is not getter wrapped
} else {
primaryExpr = naming.makeQualifiedName(primaryExpr, (TypedDeclaration)decl, Naming.NA_Q_LOCAL_INSTANCE);
selector = naming.selector((TypedDeclaration)decl);
}
}
} else if (Decl.isMethodOrSharedOrCapturedParam(decl)) {
mustUseParameter = (primaryExpr == null
&& decl.isParameter()
&& isWithinDefaultParameterExpression(decl.getContainer()));
if (!decl.isParameter()
&& (Decl.isLocalNotInitializer(decl) || (Decl.isLocalToInitializer(decl) && ((Method)decl).isDeferred()))) {
primaryExpr = null;
int flags = Naming.NA_MEMBER;
if (!isRecursiveReference(expr)) {
// Only want to quote the method name
// e.g. enum.$enum()
flags |= Naming.NA_WRAPPER_UNQUOTED;
}else if(!isReferenceInSameScope(expr)){
// always qualify it with this
flags |= Naming.NA_WRAPPER | Naming.NA_WRAPPER_WITH_THIS;
}
qualExpr = naming.makeName((Method)decl, flags);
selector = null;
} else if (decl.isToplevel()) {
primaryExpr = null;
qualExpr = naming.makeName((Method)decl, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER);
selector = null;
} else if (!isWithinInvocation()) {
selector = null;
} else {
// not toplevel, not within method, must be a class member
selector = naming.selector((Method)decl);
}
}
if (result == null) {
boolean useGetter = !(decl instanceof Method) && !mustUseField && !mustUseParameter;
if (qualExpr == null && selector == null) {
useGetter = Decl.isClassAttribute(decl) && CodegenUtil.isErasedAttribute(decl.getName());
if (useGetter) {
selector = naming.selector((TypedDeclaration)decl);
} else {
selector = naming.substitute(decl);
}
}
if (qualExpr == null) {
qualExpr = primaryExpr;
}
// FIXME: Stef has a strong suspicion that the four next methods
// should be merged since they all add a this qualifier in different
// cases
if(!mustUseParameter){
qualExpr = addQualifierForObjectMembersOfInterface(expr, decl, qualExpr);
qualExpr = addInterfaceImplAccessorIfRequired(qualExpr, expr, decl);
qualExpr = addThisQualifierIfRequired(qualExpr, expr, decl);
if (qualExpr == null && needDollarThis(expr)) {
qualExpr = makeQualifiedDollarThis((Tree.BaseMemberExpression)expr);
}
}
if (qualExpr == null && decl.isStaticallyImportable()
// make sure we only do this for things contained in a type, as otherwise
// it breaks for qualified calls to static methods in interfaces in Java 8
// it only breaks for interfaces because they are statically importable
// and not classes
&& decl.getContainer() instanceof TypeDeclaration) {
qualExpr = naming.makeTypeDeclarationExpression(null, (TypeDeclaration)decl.getContainer(), DeclNameFlag.QUALIFIED);
}
if (Decl.isPrivateAccessRequiringUpcast(expr)) {
qualExpr = makePrivateAccessUpcast(expr, qualExpr);
}
if (transformer != null) {
result = transformer.transform(qualExpr, selector);
} else {
Tree.Primary qmePrimary = null;
if (expr instanceof Tree.QualifiedMemberOrTypeExpression) {
qmePrimary = ((Tree.QualifiedMemberOrTypeExpression)expr).getPrimary();
}
if (Decl.isValueTypeDecl(qmePrimary)
// Safe operators always work on boxed things, so don't use value types
&& (expr instanceof Tree.QualifiedMemberOrTypeExpression == false
|| ((Tree.QualifiedMemberOrTypeExpression)expr).getMemberOperator() instanceof Tree.SafeMemberOp == false)
// We never want to use value types on boxed things, unless they are java arrays
&& (CodegenUtil.isUnBoxed(qmePrimary) || isJavaArray(qmePrimary.getTypeModel()))
// Java arrays length property does not go via value types
&& (!isJavaArray(qmePrimary.getTypeModel())
|| (!"length".equals(selector) && !"hashCode".equals(selector)))) {
JCExpression primTypeExpr = makeJavaType(qmePrimary.getTypeModel(), JT_NO_PRIMITIVES | JT_VALUE_TYPE);
result = makeQualIdent(primTypeExpr, selector);
result = make().Apply(List.<JCTree.JCExpression>nil(),
result,
List.<JCTree.JCExpression>of(qualExpr));
} else if (expr instanceof Tree.QualifiedMemberOrTypeExpression