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

Revision 3289, 16.7 KB (checked in by dr2chase, 11 months ago)

Feeding generated APIs to static analysis, not yet working

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