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

Revision 3291, 17.2 KB (checked in by dr2chase, 11 months ago)

[repository, ffi] Generated APIs now use proper import syntax, seem to pass static checking

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