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

Revision 3289, 33.9 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 static com.sun.fortress.interpreter.glue.WellKnownNames.defaultLibrary;
21
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.lang.reflect.AccessibleObject;
26import java.lang.reflect.Field;
27import java.lang.reflect.Method;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35import java.util.Set;
36
37import com.sun.fortress.Shell;
38import com.sun.fortress.compiler.AnalyzeResult;
39import com.sun.fortress.compiler.GlobalEnvironment;
40import com.sun.fortress.compiler.Parser;
41import com.sun.fortress.compiler.Parser.Result;
42import com.sun.fortress.compiler.index.ApiIndex;
43import com.sun.fortress.compiler.index.ComponentIndex;
44import com.sun.fortress.exceptions.MultipleStaticError;
45import com.sun.fortress.exceptions.ProgramError;
46import com.sun.fortress.exceptions.StaticError;
47import com.sun.fortress.exceptions.WrappedException;
48import com.sun.fortress.interpreter.glue.WellKnownNames;
49import com.sun.fortress.nodes.APIName;
50import com.sun.fortress.nodes.AliasedAPIName;
51import com.sun.fortress.nodes.AliasedSimpleName;
52import com.sun.fortress.nodes.Api;
53import com.sun.fortress.nodes.CompilationUnit;
54import com.sun.fortress.nodes.Component;
55import com.sun.fortress.nodes.FnDecl;
56import com.sun.fortress.nodes.Id;
57import com.sun.fortress.nodes.IdOrOp;
58import com.sun.fortress.nodes.IdOrOpOrAnonymousName;
59import com.sun.fortress.nodes.Import;
60import com.sun.fortress.nodes.ImportApi;
61import com.sun.fortress.nodes.ImportNames;
62import com.sun.fortress.nodes.ImportedNames;
63import com.sun.fortress.nodes.NodeDepthFirstVisitor_void;
64import com.sun.fortress.nodes.StaticParam;
65import com.sun.fortress.nodes.TraitObjectDecl;
66import com.sun.fortress.nodes.TraitTypeWhere;
67import com.sun.fortress.nodes_util.NodeFactory;
68import com.sun.fortress.nodes_util.NodeUtil;
69import com.sun.fortress.nodes_util.Span;
70import com.sun.fortress.repository.graph.ApiGraphNode;
71import com.sun.fortress.repository.graph.ComponentGraphNode;
72import com.sun.fortress.repository.graph.Graph;
73import com.sun.fortress.repository.graph.GraphNode;
74import com.sun.fortress.repository.graph.GraphVisitor;
75import com.sun.fortress.syntax_abstractions.parser.FortressParser;
76import com.sun.fortress.useful.Bijection;
77import com.sun.fortress.useful.Debug;
78import com.sun.fortress.useful.Fn;
79import com.sun.fortress.useful.HashBijection;
80import com.sun.fortress.useful.MultiMap;
81import com.sun.fortress.useful.Path;
82import com.sun.fortress.useful.Useful;
83import com.sun.fortress.useful.Debug.Type;
84
85import edu.rice.cs.plt.iter.IterUtil;
86import edu.rice.cs.plt.tuple.Option;
87import edu.rice.cs.plt.tuple.OptionUnwrapException;
88
89/* A graph-based repository. This repository determines the dependency structure
90 * before any components/APIs are compiled so that they can be compiled in an
91 * efficient and deterministic order.
92 *
93 * When only compiling a .fss file,
94 * the .fss depends on its importing APIs and those APIs depend
95 * on the APIs they import.
96 * When linking a .fss file, the .fss depends on its importing APIs,
97 * and components that implement those APIs are added to the graph.
98 *
99 * Elements in the graph are stored as Node depends on List<Node>
100 */
101public class GraphRepository extends StubRepository implements FortressRepository {
102
103    /* stores the nodes and their relationships */
104    private Graph<GraphNode> graph;
105    /* current source path */
106    private Path path;
107    /* underlying cache of compiled files */
108    private CacheBasedRepository cache;
109    /* true if a recompile is needed */
110    private boolean needUpdate = true;
111    /* If link is true then pull in a component for an API */
112    private boolean link = false;
113
114    ForeignJava foreignJava = new ForeignJava();
115
116    public GraphRepository(Path p, CacheBasedRepository cache) throws FileNotFoundException {
117        this.path = p;
118        this.cache = cache;
119        graph = new Graph<GraphNode>();
120        addRoots();
121    }
122
123    private static String[] roots() {
124        /* files that are dependencies of everything */
125        return defaultLibrary();
126    }
127
128    /* by default all the root APIs should be added to the graph
129     * and set as dependencies for everything else.
130     */
131    private void addRoots() throws FileNotFoundException {
132        for ( String root : roots() ){
133            APIName name = NodeFactory.makeAPIName(NodeFactory.shellSpan,root);
134            ApiGraphNode api = new ApiGraphNode(name, getApiFileDate(name));
135            try{
136                long cache_date = cache.getModifiedDateForApi(api.getName());
137                api.setApi( cache.getApi( api.getName() ), cache_date);
138            } catch ( FileNotFoundException e ){
139            } catch ( IOException e ){
140            }
141            graph.addNode( api );
142        }
143
144        for ( String root : roots() ) {
145            ApiGraphNode node =
146                (ApiGraphNode) graph.find(ApiGraphNode.key(NodeFactory.makeAPIName(NodeFactory.shellSpan,root)));
147            try{
148                for ( APIName api : dependencies(node) ){
149                    Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
150                    graph.addEdge(node, addApiGraph(api));
151                }
152            } catch ( FileNotFoundException e ){
153            } catch ( IOException e ){
154            }
155        }
156    }
157
158    @Override
159    public Map<APIName, ApiIndex> apis() {
160        return foreignJava.augmentApiMap(cache.apis.copy());
161    }
162
163    @Override
164    public Map<APIName, ComponentIndex> components() {
165        return cache.components();
166    }
167
168    /* getApi and getComponent add an API/component to the graph, find all the
169     * dependencies( via addApiGraph/addComponentGraph ) and recompile everything.
170     */
171    @Override
172    public ApiIndex getApi(APIName name) throws FileNotFoundException, IOException, StaticError {
173       Debug.debug( Debug.Type.REPOSITORY, 2, "Get API for ", name);
174       
175        ApiGraphNode node = addApiGraph(name);
176        refreshGraph();
177        try{
178            return node.getApi().unwrap();
179        } catch ( OptionUnwrapException o ){
180            throw StaticError.make( "Cannot find api " + name + " in the repository. This should not happen, please contact a developer.", "" );
181        }
182    }
183
184    @Override
185    public ComponentIndex getComponent(APIName name) throws FileNotFoundException, IOException, StaticError {
186        Debug.debug( Debug.Type.REPOSITORY, 2, "Get component for ", name );
187        ComponentGraphNode node = addComponentGraph(name);
188        refreshGraph();
189        try{
190            return node.getComponent().unwrap();
191        } catch ( OptionUnwrapException o ){
192            throw StaticError.make( "Cannot find component " + name + " in the repository. " +
193                                    "This should not happen, please contact a developer.", "" );
194        }
195    }
196
197    /* add an API node to the graph and return the node. if the API exists in the
198     * cache it is loaded, otherwise it will remain empty until it gets
199     * recompiled( probably via refreshGraph )
200     */
201    private ApiGraphNode addApiGraph( APIName name ) throws FileNotFoundException, IOException {
202        Debug.debug( Debug.Type.REPOSITORY, 2, "Add API graph ", name );
203        ApiGraphNode node = (ApiGraphNode) graph.find(ApiGraphNode.key(name));
204        if ( node == null ){
205           
206            if (foreignJava.definesApi(name)) {
207                // TODO not smart about age of native API yet
208                // Make the native API be very old, so nothing is out of date;
209                needUpdate = true;
210                node = new ApiGraphNode(name, Long.MIN_VALUE);
211                graph.addNode( node );
212                return node;
213            }
214           
215            /* a new node was added, a recompile is needed */
216            needUpdate = true;
217            node = new ApiGraphNode(name, getApiFileDate(name));
218            graph.addNode( node );
219            try{
220                /* try to load the API from the cache.
221                 * if it fails then it will be reloaded later on
222                 * in refreshGraph
223                 */
224                long cache_date = getCacheDate(node);
225                if ( cache_date > getApiFileDate(node) ){
226                    Debug.debug( Debug.Type.REPOSITORY, 2 , "Found cached version of ", node );
227                    node.setApi( cache.getApi(name), cache_date);
228                }
229            } catch ( FileNotFoundException f ){
230                /* oh well */
231            } catch ( IOException e ){
232            }
233            /* make this API depend on the APIs it imports */
234            for ( APIName api : dependencies(node) ){
235                Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
236                graph.addEdge(node, addApiGraph(api));
237            }
238            /* and depend on all the root APIs */
239            for ( String root : roots() ){
240                graph.addEdge(node, addApiGraph(NodeFactory.makeAPIName(NodeFactory.shellSpan,root)));
241            }
242        }
243
244        return node;
245    }
246
247    /* same thing, but add a component */
248    private ComponentGraphNode addComponentGraph( APIName name ) throws FileNotFoundException, IOException, StaticError {
249        Debug.debug( Debug.Type.REPOSITORY, 2, "Add component graph ", name );
250        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
251        if ( node == null ){
252            /* a new node was added, a recompile is needed */
253            needUpdate = true;
254            node = new ComponentGraphNode(name, getComponentFileDate(name));
255            graph.addNode( node );
256            try{
257                /* try to load the component from the cache.
258                 * if it fails then it will be reloaded later on
259                 * in refreshGraph
260                 */
261                long cache_date = getCacheDate(node);
262                if ( cache_date > getComponentFileDate(node) ){
263                    Debug.debug( Debug.Type.REPOSITORY, 2 , "Found cached version of ", node );
264                    node.setComponent( cache.getComponent(name), cache_date);
265                }
266            } catch ( FileNotFoundException f ){
267                /* oh well */
268            } catch ( IOException e ){
269            }
270
271            /* make this component depend on the APIs it imports */
272            for ( APIName api : dependencies(node) ){
273                nodeDependsOnApi(node, api);
274            }
275            /* and depend on all the root APIs */
276            for ( String root : roots() ){
277                nodeDependsOnApi(node, NodeFactory.makeAPIName(NodeFactory.shellSpan,root));
278            }
279        }
280
281        return node;
282    }
283
284    private void nodeDependsOnApi(ComponentGraphNode node, APIName api)
285            throws FileNotFoundException, IOException {
286        Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
287        graph.addEdge(node, addApiGraph(api));
288        boolean b = foreignJava.definesApi(api);
289        // System.err.println("b="+b);
290        if ( link && ! b){
291            Debug.debug( Debug.Type.REPOSITORY, 1, "Component ", node.getName(), " depends on API ", api );
292            // Add element, but no API
293            addComponentGraph(api);
294        }
295    }
296
297    private long getCacheDate( ApiGraphNode node ){
298        try{
299            return cache.getModifiedDateForApi(node.getName());
300        } catch ( FileNotFoundException e ){
301            return Long.MIN_VALUE;
302        }
303    }
304
305    private long getCacheDate( ComponentGraphNode node ){
306        try{
307            return cache.getModifiedDateForComponent(node.getName());
308        } catch ( FileNotFoundException e ){
309            return Long.MIN_VALUE;
310        }
311    }
312
313    private long getCacheDate( GraphNode node ){
314        try{
315            return node.accept( new CacheVisitor() );
316        } catch ( FileNotFoundException e ){
317            return Long.MIN_VALUE;
318        }
319    }
320
321    private long getComponentFileDate( ComponentGraphNode node ) throws FileNotFoundException {
322        return node.getSourceDate();
323    }
324
325    private long getComponentFileDate( APIName name ) throws FileNotFoundException {
326        return findFile( name, ProjectProperties.COMP_SOURCE_SUFFIX ).lastModified();
327    }
328
329    private long getApiFileDate( ApiGraphNode node ) throws FileNotFoundException {
330        return node.getSourceDate();
331    }
332
333    private long getApiFileDate( APIName name ) throws FileNotFoundException {
334        return findFile( name, ProjectProperties.API_SOURCE_SUFFIX ).lastModified();
335    }
336
337    public File findFile(APIName name, String suffix) throws FileNotFoundException {
338        String dotted = name.toString();
339        String slashed = dotted.replaceAll("[.]", "/");
340        slashed = slashed + "." + suffix;
341        File fdot;
342
343        Debug.debug( Debug.Type.REPOSITORY, 3, "Finding file ", name);
344        try {
345            fdot = path.findFile(slashed);
346        } catch (FileNotFoundException ex2) {
347            throw new FileNotFoundException(NodeUtil.getSpan(name) +
348                                            "\n    Could not find API " + dotted +
349                                            " in file named " + slashed +
350                                            " on path\n    " + path);
351        }
352        return fdot;
353    }
354
355    private class CacheVisitor implements GraphVisitor<Long, FileNotFoundException>{
356        public Long visit(ApiGraphNode node) throws FileNotFoundException {
357            return getCacheDate(node);
358        }
359
360        public Long visit(ComponentGraphNode node) throws FileNotFoundException {
361            return getCacheDate(node);
362        }
363    }
364
365    /* what if the file has been edited to include import statements that the cached
366     * version doesn't have? that's ok because the cached version won't be loaded unless it
367     * is newer than the file on disk.
368     */
369    private List<APIName> dependencies(ApiGraphNode node) throws FileNotFoundException, StaticError {
370        CompilationUnit cu = node.getApi().isSome() ?
371                node.getApi().unwrap().ast() :
372                    readCUFor(node, ProjectProperties.API_SOURCE_SUFFIX);
373
374        return collectApiImports((Api)cu);
375
376    }
377
378    private List<APIName> dependencies(ComponentGraphNode node) throws FileNotFoundException, StaticError {
379        CompilationUnit cu = node.getComponent().isSome() ?
380                node.getComponent().unwrap().ast() :
381                    readCUFor(node, ProjectProperties.COMP_SOURCE_SUFFIX);
382
383        return collectComponentImports((Component)cu);
384    }
385
386    private boolean inApiList( APIName name, List<ApiGraphNode> nodes ){
387        for ( ApiGraphNode node : nodes ){
388            if ( node.getName().equals( name ) ){
389                return true;
390            }
391        }
392        return false;
393    }
394
395    private boolean inComponentList( APIName name, List<ComponentGraphNode> nodes ){
396        for ( ComponentGraphNode node : nodes ){
397            if ( node.getName().equals( name ) ){
398                return true;
399            }
400        }
401        return false;
402    }
403
404    /* reparse anything that is out of date */
405    private AnalyzeResult refreshGraph() throws FileNotFoundException, IOException, StaticError {
406        AnalyzeResult result =
407            new AnalyzeResult(IterUtil.<StaticError>empty());
408        if ( needUpdate ){
409            needUpdate = false;
410            OutOfDateVisitor date = new OutOfDateVisitor();
411            for ( GraphNode node : graph.nodes() ){
412                node.accept( date );
413            }
414            List<ApiGraphNode> reparseApis = date.apis();
415            List<ComponentGraphNode> reparseComponents = sortComponents(date.components());
416            Debug.debug( Debug.Type.REPOSITORY, 1, "Out of date APIs ", reparseApis );
417            Debug.debug( Debug.Type.REPOSITORY, 1, "Out of date components ", reparseComponents );
418            /* these can be parsed all at once */
419            result = parseApis(date.outOfDateApi());
420            for ( Map.Entry<APIName, ApiIndex> entry : result.apis().entrySet() ){
421                if ( inApiList(entry.getKey(), reparseApis) ){
422                    addApi( entry.getKey(), entry.getValue() );
423                }
424            }
425
426            /* but these have to be done in a specific order due to
427             * syntax expansion requiring some components, like
428             * FortressBuiltin, being parsed.
429             * If syntax expansion was unable to execute code then all
430             * the APIs and components could be parsed at the same time.
431             */
432            for ( ComponentGraphNode node : reparseComponents ){
433                /* parseComponent will call Fortress.analyze which
434                 * will call repository.add(component). That add() will
435                 * call setComponent() on this node with the same component
436                 * that parseComponent is going to return.
437                 */
438                result = parseComponent(syntaxExpand(node));
439                for ( Map.Entry<APIName, ComponentIndex> entry : result.components().entrySet() ){
440                    if ( inComponentList( entry.getKey(), reparseComponents ) ){
441                        addComponent( entry.getKey(), entry.getValue() );
442                    }
443                }
444            }
445        }
446        return result;
447    }
448
449    private class OutOfDateVisitor implements GraphVisitor<Boolean,FileNotFoundException>{
450        private Map<GraphNode, Long> youngestSourceDependedOn;
451        private Map<GraphNode, Boolean> staleOrDependsOnStale;
452
453        public OutOfDateVisitor(){
454            youngestSourceDependedOn = new HashMap<GraphNode,Long>();
455            staleOrDependsOnStale = new HashMap<GraphNode,Boolean>();
456        }
457
458        Fn<GraphNode,Boolean> outOfDateApi() {
459            return new Fn<GraphNode,Boolean>(){
460                @Override
461                public Boolean apply(GraphNode g){
462                    return g instanceof ApiGraphNode && (staleOrDependsOnStale.get(g) ||
463                            // TODO need to fix the whole date-stamp thing for APIs.
464                            foreignJava.definesApi(((ApiGraphNode)g).getName())
465                            );
466                }
467            };
468        }
469
470        Fn<GraphNode,Boolean> outOfDateComponent() {
471            return new Fn<GraphNode,Boolean>(){
472                @Override
473                public Boolean apply(GraphNode g){
474                    return g instanceof ComponentGraphNode && staleOrDependsOnStale.get(g);
475                }
476            };
477        }
478
479        public List<ApiGraphNode> apis(){
480            return Useful.convertList(Useful.filter(youngestSourceDependedOn.keySet(), outOfDateApi()));
481        }
482        /* returns out of date components */
483        public List<ComponentGraphNode> components(){
484            return Useful.convertList(Useful.filter(youngestSourceDependedOn.keySet(), outOfDateComponent()));
485        }
486
487        private Long handle( GraphNode node ) throws FileNotFoundException {
488            if ( youngestSourceDependedOn.containsKey(node) ){
489                return youngestSourceDependedOn.get(node);
490            }
491            long youngest = node.getSourceDate();
492            youngestSourceDependedOn.put(node, youngest);
493
494            List<GraphNode> depends = graph.depends(node);
495            Debug.debug( Debug.Type.REPOSITORY, 2, node + " depends on " + depends );
496            for ( GraphNode next : depends ){
497                long dependent_youngest = handle(next);
498                if (dependent_youngest > youngest) {
499
500                    Debug.debug( Debug.Type.REPOSITORY, 1, next + " has younger source than " + next );
501                    youngest = dependent_youngest;
502                }
503            }
504
505            youngestSourceDependedOn.put(node, youngest);
506
507            return youngest;
508        }
509
510        private Boolean isStale( GraphNode node ) throws FileNotFoundException {
511            if ( staleOrDependsOnStale.containsKey(node) ){
512                return staleOrDependsOnStale.get(node);
513            }
514
515            boolean stale = youngestSourceDependedOn.get(node) > getCacheDate(node);
516
517            // If anything depended on has source that is younger than our compiled code,
518            // then this is stale.
519            if ( stale ){
520                Debug.debug( Debug.Type.REPOSITORY, 1, node + "or dependent is newer " + youngestSourceDependedOn.get(node) + " than the cache " + getCacheDate(node) );
521            }
522
523            staleOrDependsOnStale.put(node, stale);
524
525            List<GraphNode> depends = graph.depends(node);
526            Debug.debug( Debug.Type.REPOSITORY, 2, node + " depends on " + depends );
527            for ( GraphNode next : depends ){
528                boolean dependent_stale = isStale(next);
529
530                if ( dependent_stale  ){
531                    stale = true;
532                    Debug.debug( Debug.Type.REPOSITORY, 1, node + " is stale " + next + " is stale" );
533                    staleOrDependsOnStale.put(node, stale);
534                }
535            }
536            return stale;
537        }
538
539        public Boolean visit( ApiGraphNode node ) throws FileNotFoundException {
540            handle(node);
541            return isStale( node );
542        }
543
544        public Boolean visit( ComponentGraphNode node ) throws FileNotFoundException {
545            handle(node);
546            return isStale( node );
547        }
548    }
549
550    private List<ComponentGraphNode> sortComponents(List<ComponentGraphNode> nodes) throws FileNotFoundException {
551        Graph<GraphNode> componentGraph = new Graph<GraphNode>( graph, new Fn<GraphNode, Boolean>(){
552                @Override
553                public Boolean apply(GraphNode g){
554                    return g instanceof ComponentGraphNode;
555                }
556            });
557
558        /* force components that import things to depend on the component
559         * that implements that import. This is for syntax abstraction
560         * update 6/19/2008: this would prevent separate compilation, so
561         * don't set things up this way.
562         */
563        /*
564        for ( GraphNode node : componentGraph.nodes() ){
565            ComponentGraphNode comp = (ComponentGraphNode) node;
566            for ( GraphNode dependency : graph.dependencies(comp) ){
567                if ( dependency instanceof ApiGraphNode ){
568                    ComponentGraphNode next = (ComponentGraphNode) componentGraph.find( new ComponentGraphNode( ((ApiGraphNode) dependency).getName() ) );
569                    // ComponentGraphNode next = new ComponentGraphNode( ((ApiGraphNode) dependency).getName() );
570                    if ( ! next.equals( comp ) ){
571                        componentGraph.addEdge( comp, next );
572                    }
573                }
574            }
575        }
576        */
577
578        if ( Debug.isOnFor(2, Debug.Type.REPOSITORY) ){
579            Debug.debug( Debug.Type.REPOSITORY, 2, componentGraph.getDebugString() );
580        }
581
582        List<GraphNode> sorted = componentGraph.sorted();
583        List<ComponentGraphNode> rest = new ArrayList<ComponentGraphNode>();
584
585        for ( GraphNode node : sorted ){
586            ComponentGraphNode comp = (ComponentGraphNode) node;
587            if ( nodes.contains( comp ) ){
588                /* force root components to come in front of other things */
589                if ( Arrays.asList(roots()).contains(comp.getName().toString()) ){
590                    rest.add( 0, comp );
591                } else {
592                    rest.add( comp );
593                }
594            }
595        }
596
597        return rest;
598    }
599
600    private AnalyzeResult parseApis(final Fn<GraphNode, Boolean> these_apis){
601
602        List<Api> unparsed = Useful.applyToAll(graph.filter(these_apis),
603            new Fn<GraphNode, Api>(){
604                @Override
605                public Api apply(GraphNode g){
606                    return parseApi((ApiGraphNode) g);
607                }
608            });
609
610        if (unparsed.size() == 0)
611            return new AnalyzeResult(IterUtil.<StaticError>empty());
612
613        GlobalEnvironment knownApis = new GlobalEnvironment.FromMap(parsedApis());
614
615        // Can we exclude non-imported pieces of the api here?
616
617
618        List<Component> components = new ArrayList<Component>();
619        Shell shell = new Shell(this);
620        AnalyzeResult result =
621            Shell.analyze(shell.getRepository(),
622                          knownApis, unparsed, components, System.currentTimeMillis() );
623        if ( !result.isSuccessful() ){
624            throw new MultipleStaticError(result.errors());
625        }
626        return result;
627    }
628
629    /* return a parsed API */
630    private Api parseApi( ApiGraphNode node ){
631        try{
632            APIName api_name = node.getName();
633            if (foreignJava.definesApi(api_name)) {
634                return (Api) foreignJava.fakeApi(api_name).ast();
635            } else {
636                File fdot = findFile(api_name, ProjectProperties.API_SOURCE_SUFFIX);
637                CompilationUnit api = Parser.parseFileConvertExn(fdot);
638                if (api instanceof Api) {
639                    return (Api) api;
640                } else {
641                    throw StaticError.make("Unexpected parse of API " + api_name, "");
642                }
643            }
644        } catch ( FileNotFoundException e ){
645            throw new WrappedException(e);
646        } catch ( IOException e ){
647            throw new WrappedException(e);
648        }
649    }
650
651
652    /* find all parsed APIs */
653    public Map<APIName, ApiIndex> parsedApis(){
654       
655        Map<APIName, ApiIndex> apis = new HashMap<APIName, ApiIndex>();
656       
657        for ( GraphNode g :  graph.nodes()){
658            if (g instanceof ApiGraphNode) {
659                ApiGraphNode node = (ApiGraphNode) g;
660                if (node.getApi().isSome()) {
661                    apis.put( node.getName(), node.getApi().unwrap() );
662                } else if (foreignJava.definesApi(node.getName())) {
663                    apis.put(node.getName(), foreignJava.fakeApi(node.getName()));
664                }
665            }
666        }
667        return apis;
668    }
669
670    /* parse a single component. */
671    private AnalyzeResult parseComponent( Component component ) throws StaticError {
672        GlobalEnvironment knownApis = new GlobalEnvironment.FromMap(parsedApis());
673        List<Component> components = new ArrayList<Component>();
674        components.add(component);
675        long now = System.currentTimeMillis();
676        Debug.debug( Debug.Type.REPOSITORY, 1, "Parsing ", component, " at ", now );
677
678        Shell shell = new Shell(this);
679        AnalyzeResult result =
680            Shell.analyze(shell.getRepository(),
681                          knownApis, new ArrayList<Api>(), components, now );
682        if ( !result.isSuccessful() ){
683            throw new MultipleStaticError(result.errors());
684        }
685        return result;
686    }
687
688    /* parse a component and run it through syntax expansion, may
689     * invoke code( transformer expressions )
690     */
691    private Component syntaxExpand( ComponentGraphNode node ) throws FileNotFoundException, IOException {
692        Debug.debug( Debug.Type.REPOSITORY, 1, "Expand component ", node );
693
694        APIName api_name = node.getName();
695        File file = findFile(api_name, ProjectProperties.COMP_SOURCE_SUFFIX);
696        GraphRepository g1 = new GraphRepository( this.path, this.cache );
697        /* FIXME: hack to prevent infinite recursion */
698        Shell.setCurrentInterpreterRepository( g1 );
699        Result result = FortressParser.parse(api_name, file, new GlobalEnvironment.FromRepository( g1 ), verbose());
700        // Result result = FortressParser.parse(file, new GlobalEnvironment.FromRepository(this), verbose());
701        /* FIXME: hack to prevent infinite recursion */
702        Shell.setCurrentInterpreterRepository( this );
703        if (result.isSuccessful()) {
704            Debug.debug( Debug.Type.REPOSITORY, 1, "Expanded component ", node );
705            Iterator<Component> components = result.components().iterator();
706            if (components.hasNext()) return components.next();
707            throw new ProgramError("Successful parse result was nonetheless empty, file " + file.getCanonicalPath());
708        }
709        throw new ProgramError(result.errors());
710    }
711
712    /* add an API to the repository and cache it */
713    @Override
714    public void addApi(APIName name, ApiIndex definition) {
715        ApiGraphNode node = (ApiGraphNode) graph.find(ApiGraphNode.key(name));
716        if ( node == null ){
717            throw new RuntimeException("No such API '" + name + "'");
718        } else {
719            node.setApi(definition, definition.modifiedDate());
720            cache.addApi(name, definition);
721        }
722    }
723
724    /* add a component to the repository and cache it */
725    @Override
726    public void addComponent(APIName name, ComponentIndex definition){
727        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
728        if (node == null ){
729            throw new RuntimeException("No such component " + name);
730        } else {
731            node.setComponent(definition, definition.modifiedDate());
732            cache.addComponent(name, definition);
733        }
734    }
735
736    @Override
737    public void deleteComponent(APIName name) {
738        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
739        if ( node != null ){
740            cache.deleteComponent(name);
741        }
742    }
743
744    @Override
745    public ComponentIndex getLinkedComponent(APIName name) throws FileNotFoundException, IOException {
746        link = true;
747        addRootComponents();
748        ComponentIndex node = getComponent(name);
749        link = false;
750        return node;
751    }
752
753    private void addRootComponents() throws FileNotFoundException, IOException{
754        boolean added = false;
755        for ( String root : roots() ){
756            APIName name = NodeFactory.makeAPIName(NodeFactory.shellSpan,root);
757            if (null == graph.find(ApiGraphNode.key(name))) {
758                addApiGraph(name);
759            }
760            // If the API is from import-native, treat it differently than this.
761            if (link && null == graph.find(ComponentGraphNode.key(name))) {
762                addComponentGraph(name);
763            }
764//            ApiGraphNode node = (ApiGraphNode) graph.find(new ApiGraphNode(name));
765//            ComponentGraphNode comp = new ComponentGraphNode(name);
766//            try{
767//                comp.setComponent( cache.getComponent( comp.getName() ) );
768//            } catch ( FileNotFoundException e ){
769//            } catch ( IOException e ){
770//            }
771//            graph.addNode( comp );
772//            graph.addEdge( comp, node );
773        }
774    }
775
776    @Override
777    public long getModifiedDateForApi(APIName name)
778        throws FileNotFoundException {
779        return 0;
780    }
781
782    @Override
783    public long getModifiedDateForComponent(APIName name)
784        throws FileNotFoundException {
785        return 0;
786    }
787
788    @Override
789    public void clear() {
790        cache.clear();
791    }
792
793    private List<APIName> collectExplicitImports(CompilationUnit comp) {
794        List<APIName> all = new ArrayList<APIName>();
795       
796        for (Import i : comp.getImports()){
797            Option<String> opt_fl = i.getForeignLanguage();
798            boolean isNative = opt_fl.isSome();
799            if (isNative && (i instanceof ImportNames)) {
800                String fl = opt_fl.unwrap();
801                // Conditional overlap with later clause.
802                // Handle import of foreign names here.
803                // Ought to handle this case by case.
804                ImportNames ins = (ImportNames) i;
805                if("java".equalsIgnoreCase(fl)) {
806                    /*
807                     *  Don't create the API yet; its contents depend on all
808                     *  the imports.
809                     */
810                    foreignJava.processJavaImport(i, ins);
811                   
812                    // depend on the API name;
813                    // "compilation"/"reading" will get the API
814                    all.add( ins.getApiName() );
815                    continue;
816                } else if ("fortress".equalsIgnoreCase(fl)) {
817                    // do nothing, fall into normal case
818                } else {
819                    throw StaticError.make("Foreign language "+ fl + " not yet handled ", i);
820                }
821            }
822
823            if (i instanceof ImportedNames) {
824                ImportedNames names = (ImportedNames) i;
825                all.add( names.getApiName() );
826            } else { // i instanceof ImportApi
827                ImportApi apis = (ImportApi) i;
828                for (AliasedAPIName a : apis.getApis()) {
829                    all.add(a.getApiName());
830                }
831            }
832        }
833        return all;
834    }
835
836    private  List<APIName> collectComponentImports(Component comp) {
837         List<APIName> all =  collectExplicitImports(comp);
838
839         for (APIName api : comp.getExports()) {
840             all.add(api);
841         }
842         return removeExecutableApi(all);
843     }
844
845    private  List<APIName> collectApiImports(Api api) {
846        List<APIName> all =  collectExplicitImports(api);
847
848        return removeExecutableApi(all);
849    }
850
851    private CompilationUnit readCUFor(GraphNode node, String sourceSuffix) throws FileNotFoundException {
852        APIName name = node.getName();
853        File fdot = findFile(name, sourceSuffix);
854        return Parser.preparseFileConvertExn(fdot);
855    }
856
857    private static List<APIName> removeExecutableApi(List<APIName> all){
858        List<APIName> fixed = new ArrayList<APIName>();
859        for (APIName name : all){
860            if (! WellKnownNames.exportsMain(name.getText())) {
861                fixed.add(name);
862            }
863        }
864        return fixed;
865    }
866
867}
Note: See TracBrowser for help on using the browser.