/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10c.visit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import polyglot.ast.Block;
import polyglot.ast.Call;
import polyglot.ast.ClassDecl;
import polyglot.ast.ClassMember;
import polyglot.ast.ConstructorDecl;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.FieldAssign;
import polyglot.ast.FieldDecl;
import polyglot.ast.Formal;
import polyglot.ast.Id;
import polyglot.ast.MethodDecl;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Receiver;
import polyglot.ast.Special;
import polyglot.frontend.Job;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LocalDef;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.X10CompilerOptions;
import x10.ast.TypeParamNode;
import x10.ast.X10Call;
import x10.ast.X10CanonicalTypeNode;
import x10.ast.X10ConstructorDecl;
import x10.ast.X10MethodDecl;
import x10.emitter.Emitter;
import x10.extension.X10Ext_c;
import x10.types.ParameterType;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10MethodDef;
import x10.types.MethodInstance;
import x10.types.X10ParsedClassType;
import x10.util.CollectionFactory;
import polyglot.types.TypeSystem;
/**
* Generate private and super call bridge methods.
* For every private method m(a:A) in class C, generate a public static method m$P(t:C, a:A) that calls t.m(a).
* For every call to super.f() in class C, if f() is defined in a superclass A, generate a virtual public bridge
* method C.f$A$S(). Note that some class B (A <: B, B <: C) may also define a B.f$A$S() (because one of its
* methods also calls super.f()).
*/
public class InlineHelper extends ContextVisitor {
private static final String BRIDGE_TO_PRIVATE_SUFFIX = "$P";
private static final String BRIDGE_TO_SUPER_SUFFIX = "$S";
private final TypeSystem xts;
private final NodeFactory xnf;
private Map<X10MethodDef,X10MethodDef> privateBridges = CollectionFactory.<X10MethodDef,X10MethodDef>newHashMap();
private Map<MethodInstance,X10MethodDef> superBridges = CollectionFactory.<MethodInstance,X10MethodDef>newHashMap();
private Map<MethodInstance,X10MethodDecl> superDecls = CollectionFactory.<MethodInstance,X10MethodDecl>newHashMap();
public InlineHelper(Job job, TypeSystem ts, NodeFactory nf) {
super(job, ts, nf);
xts = (TypeSystem) ts;
xnf = (NodeFactory) nf;
}
@Override
protected NodeVisitor enterCall(Node parent, Node n) throws SemanticException {
return super.enterCall(parent, n);
}
@Override
public Node override(Node parent, Node n) {
// compute bridge methods
// FIXME avoid name collision
Position pos = Position.COMPILER_GENERATED;
if (n instanceof ClassDecl) {
ClassDecl d = (ClassDecl) n;
final X10ClassDef cd = d.classDef();
if (prepareForInlining(cd)) {
final List<Call> supers = new ArrayList<Call>();
for (ClassMember cm : d.body().members()) {
if (cm instanceof X10MethodDecl) {
final X10MethodDecl mdcl = (X10MethodDecl) cm;
final X10MethodDef md = mdcl.methodDef();
// compute bridge methods for private methods
if (mdcl.body() != null && prepareForInlining(md)) {
mdcl.body().visit(new NodeVisitor() {
@Override
public Node leave(Node parent, Node old, Node n, NodeVisitor v) {
if (parent instanceof Call && n instanceof Special) {
if (((Special) n).kind().equals(Special.SUPER) && Types.selfBinding(((Special) n).type()).equals(cd.thisVar())) {
Call call = (Call) parent;
if (!containsMethod(supers, call)) {
supers.add(call);
// generate bridge methods for super call
MethodInstance smi = call.methodInstance(); // this will be the superclass method
X10ClassType ct = smi.container().toClass();
// The method is non-final, because the names may clash
List<Ref<? extends Type>> formalTypes = new ArrayList<Ref<? extends Type>>(smi.formalTypes().size());
for (Type ft : smi.formalTypes()) {
formalTypes.add(Types.ref(ft));
}
List<Ref<? extends Type>> throwTypes = new ArrayList<Ref<? extends Type>>(smi.throwTypes().size());
for (Type tt : smi.throwTypes()) {
throwTypes.add(Types.ref(tt));
}
X10MethodDef nmd = xts.methodDef(smi.position(), smi.errorPosition(), Types.ref(cd.asType()),
Flags.PUBLIC, Types.ref(smi.returnType()),
makeSuperBridgeName(ct.def(), smi.name()), md.typeParameters(),
formalTypes, throwTypes, cd.thisDef(), Types.toLocalDefList(smi.formalNames()), Types.ref(smi.guard()),
Types.ref(smi.typeGuard()), smi.offerType(), null);
superBridges.put(smi, nmd);
superDecls.put(smi, mdcl);
}
}
}
return n;
}
});
}
if (!md.flags().isPrivate()) {
continue;
}
// generate bridge methods for private method
Name pmn = makePrivateBridgeName(md.name());
X10MethodDef nmd = findPrivateBridgeMethod(pmn, md);
if (nmd == null) {
nmd = createPrivateBridgeMethod(pmn, md, cd, pos);
}
privateBridges.put(md, nmd);
}
}
}
}
return null;
}
private X10MethodDef findPrivateBridgeMethod(Name pmn, X10MethodDef md) {
List<Type> argTypes = new ArrayList<Type>(md.formalTypes().size());
for (Ref<? extends Type> f : md.formalTypes()) {
argTypes.add(f.get());
}
try {
Type ct = md.container().get();
Collection<MethodInstance> existingPrivateBridges =
xts.findMethods(ct, xts.MethodMatcher(ct, pmn, argTypes, context));
if (existingPrivateBridges.isEmpty()) return null;
return existingPrivateBridges.iterator().next().x10Def();
} catch (SemanticException e) {
return null;
}
}
private X10MethodDef createPrivateBridgeMethod(Name pmn, X10MethodDef md, X10ClassDef cd, Position pos) {
Type ct = Types.instantiateTypeParametersExplicitly(cd.asType());
LocalDef ldef = null;
if (!md.flags().isStatic()) {
ldef = xts.localDef(pos, Flags.FINAL, Types.ref(ct), cd.name());
}
List<Ref<? extends Type>> argTypes = new ArrayList<Ref<? extends Type>>(md.formalTypes().size() + 1);
for (Ref<? extends Type> f : md.formalTypes()) {
argTypes.add(f);
}
if (!md.flags().isStatic()) {
argTypes.add(Types.ref(ct));
}
X10MethodDef nmd = (X10MethodDef) xts.methodDef(md.position(), md.errorPosition(), Types.ref(cd.asType()),
md.flags().clearPrivate().clearProtected().clearNative().Public().Static(),
Types.ref(md.returnType().get()), pmn, argTypes, md.throwTypes());
// check
List<ParameterType> rts = new ArrayList<ParameterType>(md.typeParameters());
if (!md.flags().isStatic()) {
X10ClassDef d2 = (X10ClassDef) cd;
for (ParameterType pt : d2.typeParameters()) {
rts.add(pt);
}
}
nmd.setTypeParameters(rts);
return nmd;
}
@Override
protected Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) throws SemanticException {
// change accessor from default and protected to public
if (n instanceof FieldDecl) {
FieldDecl f = (FieldDecl) n;
if (!f.flags().flags().isPrivate()) {
return f.flags(xnf.FlagsNode(f.position(), f.flags().flags().clearProtected().Public()));
}
}
if (n instanceof MethodDecl) {
MethodDecl m = (MethodDecl) n;
if (!m.flags().flags().isPrivate()) {
return m.flags(xnf.FlagsNode(m.position(), m.flags().flags().clearProtected().Public()));
}
}
if (n instanceof ConstructorDecl) {
ConstructorDecl c = (ConstructorDecl) n;
if (!c.flags().flags().isPrivate()) {
return c.flags(xnf.FlagsNode(c.position(), c.flags().flags().clearProtected().Public()));
}
}
// caller side
if (n instanceof Field) {
// nothing to do?
return n;
}
if (n instanceof FieldAssign) {
// nothing to do?
return n;
}
// generate bridge methods
// FIXME avoid name collision
Position pos = Position.COMPILER_GENERATED;
if (n instanceof ClassDecl) {
ClassDecl d = (ClassDecl) n;
final X10ClassDef cd = d.classDef();
if (prepareForInlining(cd)) {
List<ClassMember> members = d.body().members();
List<ClassMember> nmembers = new ArrayList<ClassMember>(members.size());
for (ClassMember cm : members) {
if (cm instanceof FieldDecl) {
FieldDecl fdcl = (FieldDecl) cm;
// XTENLANG-3076 make static fields private to avoid java programmers accidentally see uninitialized fields.
if (fdcl.flags().flags().isStatic()) {
// TODO uncomment this after generating accessor method for @PerPlace static fields (XTENLANG-3077)
// if (fdcl.flags().flags().isPublic()) {
// fdcl = fdcl.flags(xnf.FlagsNode(pos, fdcl.flags().flags().clearPublic().Private()));
// }
} else {
if (fdcl.flags().flags().isPrivate()) {
fdcl = fdcl.flags(xnf.FlagsNode(pos, fdcl.flags().flags().clearPrivate().Public()));
}
}
nmembers.add(fdcl);
}
else if (cm instanceof X10MethodDecl) {
X10MethodDecl mdcl = (X10MethodDecl) cm;
MethodDef md = mdcl.methodDef();
// generate bridge methods for private methods
nmembers.add(cm);
if (!mdcl.flags().flags().isPrivate()) {
continue;
}
X10MethodDef nmd = privateBridges.get((X10MethodDef) md);
List<Formal> formals = new ArrayList<Formal>(mdcl.formals());
Type ct = cd.asType();
ct = Types.instantiateTypeParametersExplicitly(ct);
LocalDef ldef = null;
if (!mdcl.flags().flags().isStatic()) {
ldef = xts.localDef(pos, Flags.FINAL, Types.ref(ct), cd.name());
formals.add(xnf.Formal(pos, xnf.FlagsNode(pos, Flags.FINAL), xnf.X10CanonicalTypeNode(pos, ct), xnf.Id(pos, cd.name())).localDef(ldef));
}
// copy implement to the body of the static bridge method ?
List<Expr> args = new ArrayList<Expr>();
for (Formal f : mdcl.formals()) {
args.add(xnf.Local(pos, f.name()).localInstance(f.localDef().asInstance()).type(f.type().type()));
}
List<Ref<? extends Type>> argTypes = new ArrayList<Ref<? extends Type>>(mdcl.formals().size() + 1);
for (Formal f : mdcl.formals()) {
Type t = f.type().type();
argTypes.add(f.type().typeRef());
}
if (!mdcl.flags().flags().isStatic()) {
argTypes.add(Types.ref(ct));
}
Expr call;
if (mdcl.flags().flags().isStatic()) {
call = xnf.Call(pos, xnf.CanonicalTypeNode(pos, cd.asType()),
mdcl.name(), args).methodInstance(mdcl.methodDef().asInstance()).type(mdcl.returnType().type());
} else {
call = xnf.Call(pos, xnf.Local(pos, xnf.Id(pos, cd.name())).localInstance(ldef.asInstance()).type(ct), mdcl.name(), args).methodInstance(mdcl.methodDef().asInstance()).type(mdcl.returnType().type());
}
Block body;
if (mdcl.returnType().type().isVoid()) {
body = xnf.Block(pos, xnf.Eval(pos, call));
} else {
body = xnf.Block(pos, xnf.Return(pos, call));
}
// XTENLANG-3011 propagate @Throws annotations
if (mdcl.body() != null) {
body = (Block) ((X10Ext_c) body.ext()).annotations(((X10Ext_c) mdcl.body().ext()).annotations());
}
X10MethodDecl nmdcl = xnf.MethodDecl(pos, xnf.FlagsNode(pos, mdcl.flags().flags().clearPrivate().clearProtected().clearNative().Public().Static()), mdcl.returnType(), xnf.Id(pos, nmd.name()), formals, body);
// check
List<TypeParamNode> ts = new ArrayList<TypeParamNode>(mdcl.typeParameters());
if (!mdcl.flags().flags().isStatic()) {
X10ClassDef d2 = (X10ClassDef) cd;
for (ParameterType pt : d2.typeParameters()) {
ts.add(xnf.TypeParamNode(pos, xnf.Id(pos, pt.name())).type(pt));
}
}
nmdcl = nmdcl.typeParameters(ts);
nmdcl = nmdcl.methodDef(nmd);
nmembers.add(nmdcl);
cd.addMethod(nmd);
}
else {
nmembers.add(cm);
}
}
// generate bridge methods for super call
for (MethodInstance mi : superBridges.keySet()) {
X10MethodDef nmd = superBridges.get(mi);
if (((X10ClassType) nmd.container().get()).def() != cd) continue; // check that we are in the right class
List<Formal> formals = new ArrayList<Formal>();
List<Expr> arguments = new ArrayList<Expr>(nmd.formalTypes().size());
int i = 0;
for (Ref<? extends Type> tr : nmd.formalTypes()) {
Name name = Name.make("a" + i);
LocalDef ldef = xts.localDef(pos, Flags.FINAL, tr, name);
Id id = xnf.Id(pos, name);
Formal formal = xnf.Formal(pos, xnf.FlagsNode(pos, Flags.FINAL), xnf.CanonicalTypeNode(pos, tr), id);
formals.add(formal.localDef(ldef));
arguments.add(xnf.Local(pos, id).localInstance(ldef.asInstance()).type(tr.get()));
++i;
}
Type rt = nmd.returnType().get();
Type container = mi.container();
Call call = (Call) xnf.Call(pos, xnf.Super(pos).type(container), xnf.Id(pos, mi.name()), arguments).methodInstance(mi).type(rt);
Block body;
if (rt.isVoid()) {
body = xnf.Block(pos, xnf.Eval(pos, call));
} else {
body = xnf.Block(pos, xnf.Return(pos, call));
}
// XTENLANG-3011 propagate @Throws annotations
X10MethodDecl mdcl = superDecls.get(mi);
if (mdcl.body() != null) {
body = (Block) ((X10Ext_c) body.ext()).annotations(((X10Ext_c) mdcl.body().ext()).annotations());
}
X10CanonicalTypeNode returnType = xnf.X10CanonicalTypeNode(pos, rt);
// Copy annotations of return type as well since it has @Throws annotations for native method.
returnType = (X10CanonicalTypeNode) ((X10Ext_c) returnType.ext()).annotations(((X10Ext_c) mdcl.returnType().ext()).annotations());
X10MethodDecl mdcl1 = xnf.MethodDecl(pos, xnf.FlagsNode(pos, nmd.flags()), returnType, xnf.Id(pos, nmd.name()), formals, body);
mdcl1 = mdcl1.methodDef(nmd);
// check
List<TypeParamNode> typeParams = new ArrayList<TypeParamNode>(mdcl.typeParameters());
// need to pass class type parameters, too?
mdcl1 = mdcl1.typeParameters(typeParams);
nmembers.add(mdcl1);
cd.addMethod(nmd);
}
d = d.body(d.body().members(nmembers));
d = d.classDef(cd);
}
// change class (including interface) public to make it visible at any call site
d = d.flags(xnf.FlagsNode(d.position(), d.flags().flags().clearProtected().clearPrivate().Public()));
return d;
}
// caller side
X10CompilerOptions opts = (X10CompilerOptions) job.extensionInfo().getOptions();
if (n instanceof X10Call && opts.x10_config.INLINE) {
X10Call call = (X10Call) n;
Receiver target = call.target();
MethodInstance mi = call.methodInstance();
if (mi.flags().isPrivate() && !isSameDef(mi.container(), context.currentClass(),context) && !isSameTopLevel(mi.container(), context.currentClass(), context)) {
X10MethodDef md = mi.x10Def();
Name pmn = makePrivateBridgeName(md.name());
X10MethodDef nmd = findPrivateBridgeMethod(pmn, md);
if (nmd == null) {
X10ClassDef cd = ((X10ClassType) md.container().get()).def();
nmd = createPrivateBridgeMethod(pmn, md, cd, pos);
cd.addMethod(nmd);
}
Id id = xnf.Id(pos, pmn);
List<Expr> arguments = new ArrayList<Expr>(call.arguments());
if (!mi.flags().isStatic()) {
arguments.add((Expr) target);
}
MethodInstance nmi = nmd.asInstance();
Type tt = Types.baseType(target.type());
List<Type> tas = new ArrayList<Type>();
tas.addAll(mi.typeParameters());
if (!mi.flags().isStatic() && tt instanceof X10ParsedClassType) {
tas.addAll(((X10ParsedClassType) tt).x10Def().typeParameters());
}
nmi = (MethodInstance) nmi.typeParameters(tas);
if (mi.flags().isStatic()) {
return (X10Call) xnf.Call(pos, target, id, call.arguments()).methodInstance(nmi).type(nmi.returnType());
} else {
return (X10Call) xnf.Call(pos, xnf.CanonicalTypeNode(pos, target.type()), id, arguments).methodInstance(nmi).type(nmi.returnType());
}
}
// c.super.m() --> c.xxx$yyy$C$m$S();
// TODO unimplemented
return n;
}
return n;
}
public static Name makeSuperBridgeName(final ClassDef cd, Name name) {
return Name.make(Emitter.mangleAndFlattenQName(cd.asType()) + "$" + Emitter.mangleToJava(name) + BRIDGE_TO_SUPER_SUFFIX);
}
public static Name makePrivateBridgeName(Name name) {
return Name.make(Emitter.mangleToJava(name) + BRIDGE_TO_PRIVATE_SUFFIX);
}
private static boolean isSameDef(Type t1, Type t2, Context context) {
if (t1.typeEquals(t2, context)) {
return true;
}
else if (t1 instanceof ClassType && t2 instanceof ClassType && ((ClassType)t1).def().equals(((ClassType)t2).def())) {
return true;
}
return false;
}
private static boolean isSameTopLevel(Type t1, Type t2, Context context) {
if (t1 instanceof X10ClassType && t2 instanceof X10ClassType) {
t1 = Types.baseType(t1);
t2 = Types.baseType(t2);
return isSameDef(getTopLevel((X10ClassType) t1), getTopLevel((X10ClassType) t2), context);
}
return false;
}
private static Type getTopLevel(X10ClassType t) {
X10ClassType outer = (X10ClassType) t.outer();
if (outer == null) {
return t;
}
return getTopLevel(outer);
}
private static List<Ref<? extends Type>> getRefList(List<Type> types) {
List<Ref<? extends Type>> refs = new ArrayList<Ref<? extends Type>>();
for (Type type : types) {
refs.add(Types.ref(type));
}
return refs;
}
private boolean prepareForInlining(ClassDef cd) {
for (MethodDef md : cd.methods()) {
if (md instanceof X10MethodDef) {
if (prepareForInlining((X10MethodDef) md)) {
return true;
}
}
}
return false;
}
private boolean prepareForInlining(X10MethodDef xmd) {
return true;
// return !xmd.annotationsMatching(InlineType).isEmpty();
}
private boolean containsMethod(final List<Call> calls, Call call) {
for (Call c : calls) {
if (c.methodInstance().isSameMethod(call.methodInstance(), context)) {
return true;
}
}
return false;
}
}