root/trunk/ProjectFortress/src/com/sun/fortress/repository/ForeignJava.java @ 3284

Revision 3284, 15.4 KB (checked in by dr2chase, 11 months ago)

[repository/foreign] more generated APIs for foreign code; still lacks correct incorporation of generated apis into global environments, perhaps also a caching issue

Line 
1/*******************************************************************************
2    Copyright 2009 Sun Microsystems, Inc.,
3    4150 Network Circle, Santa Clara, California 95054, U.S.A.
4    All rights reserved.
5
6    U.S. Government Rights - Commercial software.
7    Government users are subject to the Sun Microsystems, Inc. standard
8    license agreement and applicable provisions of the FAR and its supplements.
9
10    Use is subject to license terms.
11
12    This distribution may include materials developed by third parties.
13
14    Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
15    trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
16 ******************************************************************************/
17
18package com.sun.fortress.repository;
19
20import java.lang.reflect.Field;
21import java.lang.reflect.Method;
22import java.math.BigInteger;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.HashMap;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29
30import com.sun.fortress.compiler.IndexBuilder;
31import com.sun.fortress.compiler.index.ApiIndex;
32import com.sun.fortress.exceptions.StaticError;
33import com.sun.fortress.nodes.APIName;
34import com.sun.fortress.nodes.AliasedAPIName;
35import com.sun.fortress.nodes.AliasedSimpleName;
36import com.sun.fortress.nodes.Api;
37import com.sun.fortress.nodes.BaseType;
38import com.sun.fortress.nodes.Decl;
39import com.sun.fortress.nodes.Expr;
40import com.sun.fortress.nodes.FnDecl;
41import com.sun.fortress.nodes.Id;
42import com.sun.fortress.nodes.IdOrOpOrAnonymousName;
43import com.sun.fortress.nodes.Import;
44import com.sun.fortress.nodes.ImportApi;
45import com.sun.fortress.nodes.ImportNames;
46import com.sun.fortress.nodes.Param;
47import com.sun.fortress.nodes.StaticParam;
48import com.sun.fortress.nodes.TraitObjectDecl;
49import com.sun.fortress.nodes.TraitTypeWhere;
50import com.sun.fortress.nodes.Type;
51import com.sun.fortress.nodes.WhereClause;
52import com.sun.fortress.nodes_util.Modifiers;
53import com.sun.fortress.nodes_util.NodeFactory;
54import com.sun.fortress.nodes_util.NodeUtil;
55import com.sun.fortress.nodes_util.Span;
56import com.sun.fortress.useful.Bijection;
57import com.sun.fortress.useful.HashBijection;
58import com.sun.fortress.useful.MultiMap;
59import com.sun.fortress.useful.Useful;
60
61import edu.rice.cs.plt.tuple.Option;
62
63public class ForeignJava {
64
65    /** given an API Name, what Java class does it import?  (Name of existing
66     * Java class, not its wrapper.)
67     */
68    MultiMap<APIName, Class> javaImplementedAPIs = new MultiMap<APIName, Class>();
69    MultiMap<APIName, APIName> generatedImports = new MultiMap<APIName, APIName>();
70   
71    /**
72     * Given an API Name, what are the decls in it?
73     * This includes both trait (class) decls and function (static method) decls.
74     */
75    MultiMap<APIName, Decl> apiToStaticDecls = new MultiMap<APIName, Decl>();
76
77    /**
78     * Given a Java class, what items from it were requested?
79     * Possibilities are:
80     * 1) Non-empty strings; those particular things.
81     * 2) Empty string; the entire class.
82     * 3) Nothing at all; an opaque import.
83     */
84    MultiMap<Class, String> itemsFromClasses = new MultiMap<Class, String>();
85   
86    Bijection<Method, FnDecl> methodToDecl = new HashBijection<Method, FnDecl>();
87    Bijection<Class, TraitObjectDecl> classToTraitDecl = new HashBijection<Class, TraitObjectDecl>();
88
89    /* Special cases
90     *
91     * Boolean
92     * Byte
93     * Character
94     * Short
95     * Int
96     * Long
97     * Float
98     * Double
99     *
100     * all of the above, .TYPE (the primitives)
101     *
102     *
103     * String
104     */
105   
106    static Span span = NodeFactory.internalSpan;
107
108    static Map<Class, Type> specialCases = new HashMap<Class, Type>();
109    static APIName fortLib =
110        NodeFactory.makeAPIName(span, "FortressLibrary");
111   
112    static void s(Class cl, APIName api, String str) {
113        specialCases.put(cl, NodeFactory.makeTraitType(span, false, NodeFactory.makeId(span, api, str)));
114    }
115   
116    static {
117        s(Boolean.class, fortLib, "Boolean");
118        s(Boolean.TYPE, fortLib, "Boolean");
119        s(Integer.class, fortLib, "ZZ32");
120        s(Integer.TYPE, fortLib, "ZZ32");
121        s(Long.class, fortLib, "ZZ64");
122        s(Long.TYPE, fortLib, "ZZ64");
123        s(Float.class, fortLib, "RR32");
124        s(Float.TYPE, fortLib, "RR32");
125        s(Double.class, fortLib, "RR64");
126        s(Double.TYPE, fortLib, "RR64");
127        s(String.class, fortLib, "String");
128        s(BigInteger.class, fortLib, "ZZ");
129        specialCases.put(Void.TYPE, NodeFactory.makeVoidType(span));
130    }
131   
132    APIName packageToAPIName(Package p) {
133        return NodeFactory.makeAPIName(span, p.getName());
134    }
135
136    void processJavaImport(Import i, ImportNames ins) {
137        APIName pkg_name = ins.getApiName();
138        String pkg_name_string = pkg_name.getText();
139        List<AliasedSimpleName> names = ins.getAliasedNames();
140        for (AliasedSimpleName name : names) {
141            Option<IdOrOpOrAnonymousName> opt_alias = name.getAlias();
142            if (opt_alias.isSome()) {
143                throw StaticError.make(
144                "Import aliasing not yet implemented for foreign imports ", i);
145            }
146
147            IdOrOpOrAnonymousName imported = name.getName();
148            Option<APIName> dotted_prefix = imported.getApiName();
149            String suffix = NodeUtil.nameString(imported);
150            /*
151             * Okay, so this means that the "api" being imported
152             * is pkg_name, and the class is specified either as
153             * the first part of the prefix (which looks like an
154             * APIName) or the suffix.
155             *
156             * Possible cases:
157             *
158             * Aclass.AnInnerClass (Map.Entry)
159             * Aclass.AStaticFunction
160             * Aclass.AField
161             * Aclass.AnInner*.{AStaticFunction,AField}
162             */
163           
164            /*
165             * More hints.
166             * The api part of the name, corresponds to a package name.
167             * So, if it is an inner class, that dot has to come
168             * in the suffix.
169             *
170             * Inner classes expect a dollar sign where the dot
171             * appears in the Java text.
172             */
173           
174            /*
175             * There can be static inner classes,
176             * static functions,
177             * and static fields,
178             * all with the same dotted name.
179             *
180             * For now, try the static inner class first,
181             * then the function,
182             * then the fields.
183             */
184           
185           
186            int last_dot = suffix.length();
187           
188            /* Try replacing fewer and fewer dots with $; prefer the
189             * "classiest" answer.
190             */
191            Class imported_class = null;
192            while (last_dot > 0) {
193                String candidate_class = suffix.substring(0,last_dot);
194                candidate_class = pkg_name_string + "." + Useful.replace(candidate_class, ".", "$");
195                    try {
196                        imported_class = Class.forName(candidate_class);
197                        break;
198                        // exit with imported_class and last_dot
199                    } catch (ClassNotFoundException e) {
200                        // failed to get this one
201                    }
202                last_dot = suffix.lastIndexOf('.', last_dot);
203            }
204            if (imported_class == null) {
205                // Could not match a class to any prefix
206                throw StaticError.make("Could not find any Java package.class prefix of  "+ pkg_name_string + "." + suffix, i);
207            }
208            /* imported_class specifies the class,
209             * the item is the unused portion of the suffix.
210             */
211            String imported_item = suffix.substring(last_dot);
212            if (imported_item.length() == 0) {
213               
214            } else if (imported_item.startsWith(".", 0)) {
215                imported_item = imported_item.substring(1);
216            } else {
217                throw StaticError.make("Internal error processing imported item " + imported_item, i);
218            }
219            /* Record import of (part of) pkg_name_string.
220             * Class imported_class
221             * item imported_item -- entire class if "".
222             */
223            // TODO
224            recurOnClass(pkg_name, imported_class, imported_item);
225           
226        } /* for name : names */
227        System.err.println("javaImplementedAPIs="+javaImplementedAPIs);
228        System.err.println("itemsFromClasses="+itemsFromClasses);
229    }
230
231    /**
232     * @param imported_class
233     */
234    private Type recurOnOpaqueClass(APIName importing_package, Class imported_class) {
235        // Need to special-case for primitives, String, Object, void.
236        // Also, not a bijection.
237        Type  t = specialCases.get(imported_class);
238        if (t != null)
239            return t;
240       
241        Package p = imported_class.getPackage();
242        APIName api_name = packageToAPIName(p);
243       
244        /* Though the class may have been previously referenced, the import
245         * may still need to be recorded.  Re-recording an existing import
246         * is harmless.
247         */
248        generatedImports.putItem(importing_package, api_name);
249       
250        Id name = NodeFactory.makeId(span, imported_class.getSimpleName());
251       
252        /*
253         *  Has there (not) been any reference to this class previously?
254         *  If no reference, then record that it is needed (opaquely) and
255         *  create a trivial declaration for it, and enter the declaration
256         *  into the appropriate places.
257         */
258        if (!itemsFromClasses.containsKey(imported_class)) {
259            itemsFromClasses.putKey(imported_class);
260            // Construct a minimal trait declaration for this class.
261            classToTraitType(imported_class, api_name, name, Collections.<Decl>emptyList());
262        };
263       
264        name = NodeFactory.makeId(api_name, name);
265        // Note: a TraitType is a reference to a trait.
266        return NodeFactory.makeTraitType(span, false, name);
267    }
268
269    private void classToTraitType(Class imported_class, APIName api_name,
270            Id name, List<Decl> decls) {
271        List<StaticParam> sparams = Collections.emptyList();
272        List<TraitTypeWhere> extendsC = Collections.emptyList();
273        Modifiers mods = Modifiers.None;
274        Option<WhereClause> whereC = Option.none();
275        List<BaseType> excludesC = Collections.emptyList();
276        Option<List<BaseType>> comprisesC = Option.none();
277        TraitObjectDecl tod = NodeFactory.makeTraitDecl (span, mods, name, sparams, extendsC, whereC, decls, excludesC, comprisesC);
278        // Need to fake up an API for this class, too.
279        classToTraitDecl.put(imported_class, tod);
280        apiToStaticDecls.putItem(api_name, tod);
281        javaImplementedAPIs.putItem(api_name, imported_class);
282    }
283   
284    private void recurOnClass(APIName pkg_name, Class imported_class, String imported_item) {
285        Set<String> old = itemsFromClasses.get(imported_class);
286       
287        /*
288         * Import of a class, non-opaquely.
289         * Keep track of which items, specifically, are imported.
290         * Note that the empty string is a valid item, it means "the class itself".
291         */
292        if (old != null && old.size() > 0) {
293            itemsFromClasses.putItem(imported_class, imported_item);
294            return;
295        }
296       
297        /*
298         * Class not seen before (except perhaps as an opaque import)
299         * Recursively visit all of its parts.
300         */
301        itemsFromClasses.putItem(imported_class, imported_item);
302
303        Method[] methods = imported_class.getDeclaredMethods();
304        Field[] fields = imported_class.getDeclaredFields();
305        Class[] interfaces = imported_class.getInterfaces();
306        Class super_class = imported_class.getSuperclass();
307       
308        ArrayList<Decl> trait_decls = new ArrayList<Decl>();
309       
310        for (Method m : methods) {
311            if (isPublic(m.getModifiers())) {
312                if (isStatic(m.getModifiers())) {
313                 // static goes to api-indexed set
314                    FnDecl decl = recurOnMethod(pkg_name, imported_class, m, true);
315                    apiToStaticDecls.putItem(pkg_name, decl);
316                } else {
317                    // non static goes to declaration pile for class
318                    FnDecl decl = recurOnMethod(pkg_name, imported_class, m, false);
319                    trait_decls.add(decl);
320                }
321            }
322        }
323       
324        Id name = NodeFactory.makeId(span, imported_class.getSimpleName());
325        classToTraitType(imported_class, pkg_name, name, trait_decls);
326       
327    }
328
329    private FnDecl recurOnMethod(APIName importing_package, Class cl, Method m, boolean is_static) {
330        if (methodToDecl.containsKey(m))
331            return methodToDecl.get(m);
332       
333        Class rt = m.getReturnType();
334        Class[] pts = m.getParameterTypes();
335        // To construct a FnDecl, need to get names for all the other types
336        Type return_type = recurOnOpaqueClass(importing_package, rt);
337       
338        List<Param> params = new ArrayList<Param>(pts.length);
339        int i = 0;
340        for (Class pt : pts) {
341            Type type = recurOnOpaqueClass(importing_package, pt);
342            Id id = NodeFactory.makeId(span, "p"+(i++));
343            Param p = NodeFactory.makeParam(id, type);
344            params.add(p);
345        }
346        Id id = is_static ?
347                NodeFactory.makeId(span, cl.getSimpleName()+"."+ m.getName()) :
348            NodeFactory.makeId(span, m.getName());
349        FnDecl fndecl = NodeFactory.makeFnDecl(span, Modifiers.None,
350                id, params,Option.some(return_type), Option.<Expr>none());
351        methodToDecl.put(m, fndecl);
352        return fndecl;
353    }
354
355    private boolean isPublic(int modifiers) {
356        return 0 != (modifiers & java.lang.reflect.Modifier.PUBLIC);
357    }
358   
359    private boolean isStatic(int modifiers) {
360        return 0 != (modifiers & java.lang.reflect.Modifier.STATIC);
361    }
362
363    public boolean definesApi(APIName name) {
364        return javaImplementedAPIs.containsKey(name);
365    }
366
367    public ApiIndex fakeApi(APIName name) {
368        Set<Class> classes = javaImplementedAPIs.get(name);
369        // Need to generate wrappers for all these classes,
370        //  if they do not already exist.
371
372        List<Import> imports = new ArrayList<Import>();
373        for (APIName a : generatedImports.get(name)) {
374            AliasedAPIName aan = NodeFactory.makeAliasedAPIName(a);
375            /*
376             * Hoping to lie, slightly, to static analysis.
377             * This is technically speaking a "foreign" import,
378             * but the import is already known to the ForeignJava
379             * data structures, and this allows use of fully qualified
380             * (hence unambiguous) references to classes from other packages
381             * in the generated API.
382             *
383             * So, one lie -- no foreign annotation.
384             */
385            ImportApi iapi = NodeFactory.makeImportApi(span, Option.<String>none(), Useful.list(aan));
386            imports.add(iapi);
387        }
388       
389        List<Decl> decls = new ArrayList<Decl>();
390        for (Decl d : apiToStaticDecls.get(name)) {
391            decls.add(d);
392        }
393        Api a =  NodeFactory.makeApi( span,  name,
394                    imports,
395                    decls);
396       
397        return IndexBuilder.builder.buildApiIndex(a, Long.MIN_VALUE+2);
398       
399    }
400
401}
Note: See TracBrowser for help on using the browser.