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

Revision 3288, 16.4 KB (checked in by dr2chase, 11 months ago)

[repository] native imports generate fake apis, sort of

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?  (Name of existing
67     * Java class, not its wrapper.)
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        Id name = NodeFactory.makeId(span, imported_class.getSimpleName());
266       
267        /*
268         *  Has there (not) been any reference to this class previously?
269         *  If no reference, then record that it is needed (opaquely) and
270         *  create a trivial declaration for it, and enter the declaration
271         *  into the appropriate places.
272         */
273        if (!itemsFromClasses.containsKey(imported_class)) {
274            itemsFromClasses.putKey(imported_class);
275            // Construct a minimal trait declaration for this class.
276            classToTraitType(imported_class, api_name, name, Collections.<Decl>emptyList());
277        };
278       
279        name = NodeFactory.makeId(api_name, name);
280        // Note: a TraitType is a reference to a trait.
281        return NodeFactory.makeTraitType(span, false, name);
282    }
283
284    private void classToTraitType(Class imported_class, APIName api_name,
285            Id name, List<Decl> decls) {
286        List<StaticParam> sparams = Collections.emptyList();
287        List<TraitTypeWhere> extendsC = Collections.emptyList();
288        Modifiers mods = Modifiers.None;
289        Option<WhereClause> whereC = Option.none();
290        List<BaseType> excludesC = Collections.emptyList();
291        Option<List<BaseType>> comprisesC = Option.none();
292        TraitDecl td = NodeFactory.makeTraitDecl (span, mods, name, sparams, extendsC, whereC, decls, excludesC, comprisesC);
293        // Need to fake up an API for this class, too.
294        classToTraitDecl.put(imported_class, td);
295        apiToStaticDecls.putItem(api_name, td);
296        javaImplementedAPIs.putItem(api_name, imported_class);
297       
298        /* Invalidate any old entry.
299         * Let's hope this does not cause problems down the line.
300         */
301        cachedFakeApis.remove(api_name);
302    }
303   
304    private void recurOnClass(APIName pkg_name, Class imported_class, String imported_item) {
305        Set<String> old = itemsFromClasses.get(imported_class);
306       
307        /*
308         * Import of a class, non-opaquely.
309         * Keep track of which items, specifically, are imported.
310         * Note that the empty string is a valid item, it means "the class itself".
311         */
312        if (old != null && old.size() > 0) {
313            itemsFromClasses.putItem(imported_class, imported_item);
314            return;
315        }
316       
317        /*
318         * Class not seen before (except perhaps as an opaque import)
319         * Recursively visit all of its parts.
320         */
321        itemsFromClasses.putItem(imported_class, imported_item);
322
323        Method[] methods = imported_class.getDeclaredMethods();
324        Field[] fields = imported_class.getDeclaredFields();
325        Class[] interfaces = imported_class.getInterfaces();
326        Class super_class = imported_class.getSuperclass();
327       
328        ArrayList<Decl> trait_decls = new ArrayList<Decl>();
329       
330        for (Method m : methods) {
331            if (isPublic(m.getModifiers())) {
332                if (isStatic(m.getModifiers())) {
333                 // static goes to api-indexed set
334                    FnDecl decl = recurOnMethod(pkg_name, imported_class, m, true);
335                    apiToStaticDecls.putItem(pkg_name, decl);
336                } else {
337                    // non static goes to declaration pile for class
338                    FnDecl decl = recurOnMethod(pkg_name, imported_class, m, false);
339                    trait_decls.add(decl);
340                }
341            }
342        }
343       
344        Id name = NodeFactory.makeId(span, imported_class.getSimpleName());
345        classToTraitType(imported_class, pkg_name, name, trait_decls);
346       
347    }
348
349    private FnDecl recurOnMethod(APIName importing_package, Class cl, Method m, boolean is_static) {
350        if (methodToDecl.containsKey(m))
351            return methodToDecl.get(m);
352       
353        Class rt = m.getReturnType();
354        Class[] pts = m.getParameterTypes();
355        // To construct a FnDecl, need to get names for all the other types
356        Type return_type = recurOnOpaqueClass(importing_package, rt);
357       
358        List<Param> params = new ArrayList<Param>(pts.length);
359        int i = 0;
360        for (Class pt : pts) {
361            Type type = recurOnOpaqueClass(importing_package, pt);
362            Id id = NodeFactory.makeId(span, "p"+(i++));
363            Param p = NodeFactory.makeParam(id, type);
364            params.add(p);
365        }
366        Id id = is_static ?
367                NodeFactory.makeId(span, cl.getSimpleName()+"."+ m.getName()) :
368            NodeFactory.makeId(span, m.getName());
369        FnDecl fndecl = NodeFactory.makeFnDecl(span, Modifiers.None,
370                id, params,Option.some(return_type), Option.<Expr>none());
371        methodToDecl.put(m, fndecl);
372        return fndecl;
373    }
374
375    private boolean isPublic(int modifiers) {
376        return 0 != (modifiers & java.lang.reflect.Modifier.PUBLIC);
377    }
378   
379    private boolean isStatic(int modifiers) {
380        return 0 != (modifiers & java.lang.reflect.Modifier.STATIC);
381    }
382
383    public boolean definesApi(APIName name) {
384        return javaImplementedAPIs.containsKey(name);
385    }
386
387    public ApiIndex fakeApi(APIName name) {
388        ApiIndex result = cachedFakeApis.get(name);
389        if (result == null) {
390
391            Set<Class> classes = javaImplementedAPIs.get(name);
392            // Need to generate wrappers for all these classes,
393            // if they do not already exist.
394
395            List<Import> imports = new ArrayList<Import>();
396            Set<APIName> gi = generatedImports.get(name);
397            if (gi != null)
398            for (APIName a : gi) {
399                AliasedAPIName aan = NodeFactory.makeAliasedAPIName(a);
400                /*
401                 * Hoping to lie, slightly, to static analysis. This is
402                 * technically speaking a "foreign" import, but the import is
403                 * already known to the ForeignJava data structures, and this
404                 * allows use of fully qualified (hence unambiguous) references
405                 * to classes from other packages in the generated API.
406                 *
407                 * So, one lie -- no foreign annotation.
408                 */
409                ImportApi iapi = NodeFactory.makeImportApi(span, Option
410                        .<String> none(), Useful.list(aan));
411                imports.add(iapi);
412            }
413
414            List<Decl> decls = new ArrayList<Decl>();
415            for (Decl d : apiToStaticDecls.get(name)) {
416                decls.add(d);
417            }
418            Api a = NodeFactory.makeApi(span, name, imports, decls);
419
420            result = IndexBuilder.builder.buildApiIndex(a, Long.MIN_VALUE + 2);
421            cachedFakeApis.put(name, result);
422        }
423        return result;
424       
425    }
426   
427    public Map<APIName, ApiIndex> augmentApiMap(Map<APIName, ApiIndex> map) {
428        for (APIName a : javaImplementedAPIs.keySet()) {
429            map.put(a, fakeApi(a));
430        }
431        return map;
432    }
433
434}
Note: See TracBrowser for help on using the browser.