ModuleFinder.java revision 3294:9adfb22ff08f
1176669Spiso/*
2176669Spiso * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3176669Spiso * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4176669Spiso *
5176669Spiso * This code is free software; you can redistribute it and/or modify it
6176669Spiso * under the terms of the GNU General Public License version 2 only, as
7176669Spiso * published by the Free Software Foundation.  Oracle designates this
8176669Spiso * particular file as subject to the "Classpath" exception as provided
9176669Spiso * by Oracle in the LICENSE file that accompanied this code.
10176669Spiso *
11176669Spiso * This code is distributed in the hope that it will be useful, but WITHOUT
12176669Spiso * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13176669Spiso * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14176669Spiso * version 2 for more details (a copy is included in the LICENSE file that
15176669Spiso * accompanied this code).
16176669Spiso *
17176669Spiso * You should have received a copy of the GNU General Public License version
18176669Spiso * 2 along with this work; if not, write to the Free Software Foundation,
19176669Spiso * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20176669Spiso *
21176669Spiso * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22176669Spiso * or visit www.oracle.com if you need additional information or have any
23176669Spiso * questions.
24176669Spiso */
25176669Spisopackage com.sun.tools.javac.code;
26176669Spiso
27176669Spisoimport java.io.IOException;
28176669Spisoimport java.util.Arrays;
29176669Spisoimport java.util.HashMap;
30176669Spisoimport java.util.Iterator;
31176669Spisoimport java.util.Map;
32176669Spisoimport java.util.NoSuchElementException;
33176669Spisoimport java.util.Set;
34176669Spiso
35176669Spisoimport javax.tools.JavaFileManager;
36176669Spisoimport javax.tools.JavaFileManager.Location;
37176669Spisoimport javax.tools.JavaFileObject;
38176669Spisoimport javax.tools.JavaFileObject.Kind;
39200580Sluigiimport javax.tools.StandardLocation;
40200580Sluigi
41176669Spisoimport com.sun.tools.javac.code.Symbol.CompletionFailure;
42176669Spisoimport com.sun.tools.javac.code.Symbol.ModuleSymbol;
43176669Spisoimport com.sun.tools.javac.resources.CompilerProperties.Errors;
44176669Spisoimport com.sun.tools.javac.resources.CompilerProperties.Fragments;
45176669Spisoimport com.sun.tools.javac.util.Context;
46176669Spisoimport com.sun.tools.javac.util.JCDiagnostic;
47176669Spisoimport com.sun.tools.javac.util.JCDiagnostic.Fragment;
48176669Spisoimport com.sun.tools.javac.util.List;
49176669Spisoimport com.sun.tools.javac.util.ListBuffer;
50176669Spisoimport com.sun.tools.javac.util.Log;
51176669Spisoimport com.sun.tools.javac.util.Name;
52243401Sglebiusimport com.sun.tools.javac.util.Names;
53243401Sglebiusimport com.sun.tools.javac.util.StringUtils;
54176669Spiso
55176669Spisoimport static com.sun.tools.javac.code.Kinds.Kind.*;
56255395Strociny
57176669Spiso/**
58200909Sluigi *  This class provides operations to locate module definitions
59176669Spiso *  from the source and class files on the paths provided to javac.
60176669Spiso *
61176669Spiso *  <p><b>This is NOT part of any supported API.
62176669Spiso *  If you write code that depends on this, you do so at your own risk.
63200897Sluigi *  This code and its internal interfaces are subject to change or
64176669Spiso *  deletion without notice.</b>
65255395Strociny */
66255395Strocinypublic class ModuleFinder {
67200897Sluigi    /** The context key for the module finder. */
68200909Sluigi    protected static final Context.Key<ModuleFinder> moduleFinderKey = new Context.Key<>();
69176669Spiso
70200897Sluigi    /** The log to use for verbose output. */
71176669Spiso    private final Log log;
72200909Sluigi
73200909Sluigi    /** The symbol table. */
74200909Sluigi    private final Symtab syms;
75200909Sluigi
76200909Sluigi    /** The name table. */
77200909Sluigi    private final Names names;
78200909Sluigi
79200909Sluigi    private final ClassFinder classFinder;
80200909Sluigi
81200909Sluigi    /** Access to files
82200909Sluigi     */
83176669Spiso    private final JavaFileManager fileManager;
84200909Sluigi
85176669Spiso    private final JCDiagnostic.Factory diags;
86200909Sluigi
87176669Spiso    /** Get the ModuleFinder instance for this invocation. */
88176669Spiso    public static ModuleFinder instance(Context context) {
89200897Sluigi        ModuleFinder instance = context.get(moduleFinderKey);
90200897Sluigi        if (instance == null)
91200897Sluigi            instance = new ModuleFinder(context);
92176669Spiso        return instance;
93200897Sluigi    }
94176669Spiso
95200897Sluigi    /** Construct a new module finder. */
96200909Sluigi    protected ModuleFinder(Context context) {
97176669Spiso        context.put(moduleFinderKey, this);
98200897Sluigi        names = Names.instance(context);
99200897Sluigi        syms = Symtab.instance(context);
100200909Sluigi        fileManager = context.get(JavaFileManager.class);
101200909Sluigi        log = Log.instance(context);
102200897Sluigi        classFinder = ClassFinder.instance(context);
103200897Sluigi
104176669Spiso        diags = JCDiagnostic.Factory.instance(context);
105176669Spiso    }
106176669Spiso
107176669Spiso    class ModuleLocationIterator implements Iterator<Set<Location>> {
108176669Spiso        StandardLocation outer;
109176669Spiso        Set<Location> next = null;
110176669Spiso
111176669Spiso        Iterator<StandardLocation> outerIter = Arrays.asList(
112176669Spiso                StandardLocation.MODULE_SOURCE_PATH,
113176669Spiso                StandardLocation.UPGRADE_MODULE_PATH,
114176669Spiso                StandardLocation.SYSTEM_MODULES,
115176669Spiso                StandardLocation.MODULE_PATH
116176669Spiso        ).iterator();
117176669Spiso        Iterator<Set<Location>> innerIter = null;
118176669Spiso
119176669Spiso        @Override
120176669Spiso        public boolean hasNext() {
121176669Spiso            while (next == null) {
122176669Spiso                while (innerIter == null || !innerIter.hasNext()) {
123176669Spiso                    if (outerIter.hasNext()) {
124176669Spiso                        outer = outerIter.next();
125176669Spiso                        try {
126176669Spiso                            innerIter = fileManager.listModuleLocations(outer).iterator();
127176669Spiso                        } catch (IOException e) {
128176669Spiso                            System.err.println("error listing module locations for " + outer + ": " + e);  // FIXME
129176669Spiso                        }
130176669Spiso                    } else
131176669Spiso                        return false;
132176669Spiso                }
133176669Spiso
134176669Spiso                if (innerIter.hasNext())
135176669Spiso                    next = innerIter.next();
136200909Sluigi            }
137176669Spiso            return true;
138200909Sluigi        }
139176669Spiso
140176669Spiso        @Override
141176669Spiso        public Set<Location> next() {
142176669Spiso            hasNext();
143220837Sglebius            if (next != null) {
144176669Spiso                Set<Location> result = next;
145176669Spiso                next = null;
146176669Spiso                return result;
147176669Spiso            }
148176669Spiso            throw new NoSuchElementException();
149176669Spiso        }
150176669Spiso
151176669Spiso    }
152176669Spiso
153176669Spiso    ModuleLocationIterator moduleLocationIterator = new ModuleLocationIterator();
154176669Spiso
155176669Spiso    public ModuleSymbol findModule(Name name) {
156176669Spiso        return findModule(syms.enterModule(name));
157176669Spiso    }
158176669Spiso
159176669Spiso    public ModuleSymbol findModule(ModuleSymbol msym) {
160176669Spiso        if (msym.kind != ERR && msym.sourceLocation == null && msym.classLocation == null) {
161176669Spiso            // fill in location
162176669Spiso            List<ModuleSymbol> list = scanModulePath(msym);
163176669Spiso            if (list.isEmpty()) {
164176669Spiso                msym.kind = ERR;
165176669Spiso            }
166176669Spiso        }
167176669Spiso        if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
168176669Spiso            // fill in module-info
169176669Spiso            findModuleInfo(msym);
170176669Spiso        }
171200909Sluigi        return msym;
172176669Spiso    }
173176669Spiso
174176669Spiso    public List<ModuleSymbol> findAllModules() {
175176669Spiso        List<ModuleSymbol> list = scanModulePath(null);
176176669Spiso        for (ModuleSymbol msym: list) {
177176669Spiso            if (msym.kind != ERR && msym.module_info.sourcefile == null && msym.module_info.classfile == null) {
178176669Spiso                // fill in module-info
179176669Spiso                findModuleInfo(msym);
180176669Spiso            }
181176669Spiso        }
182176669Spiso        return list;
183176669Spiso    }
184176669Spiso
185200909Sluigi    public ModuleSymbol findSingleModule() {
186176669Spiso        try {
187200909Sluigi            JavaFileObject src_fo = getModuleInfoFromLocation(StandardLocation.SOURCE_PATH, Kind.SOURCE);
188200909Sluigi            JavaFileObject class_fo = getModuleInfoFromLocation(StandardLocation.CLASS_OUTPUT, Kind.CLASS);
189200909Sluigi            JavaFileObject fo = (src_fo == null) ? class_fo
190200909Sluigi                    : (class_fo == null) ? src_fo
191200909Sluigi                            : classFinder.preferredFileObject(src_fo, class_fo);
192200909Sluigi
193200909Sluigi            ModuleSymbol msym;
194200909Sluigi            if (fo == null) {
195200909Sluigi                msym = syms.unnamedModule;
196200909Sluigi            } else {
197200909Sluigi                // Note: the following may trigger a re-entrant call to Modules.enter
198200909Sluigi//                msym = new ModuleSymbol();
199200909Sluigi//                ClassSymbol info = new ClassSymbol(Flags.MODULE, names.module_info, msym);
200200909Sluigi//                info.modle = msym;
201176669Spiso//                info.classfile = fo;
202200897Sluigi//                info.members_field = WriteableScope.create(info);
203176669Spiso//                msym.module_info = info;
204176669Spiso                msym = ModuleSymbol.create(null, names.module_info);
205176669Spiso                msym.module_info.classfile = fo;
206244569Smelifaro                msym.completer = sym -> classFinder.fillIn(msym.module_info);
207244569Smelifaro//                // TODO: should we do the following here, or as soon as we find the name in
208244569Smelifaro//                // the source or class file?
209244569Smelifaro//                // Consider the case when the class/source path module shadows one on the
210244569Smelifaro//                // module source path
211244569Smelifaro//                if (syms.modules.get(msym.name) != null) {
212244569Smelifaro//                    // error: module already defined
213176669Spiso//                    System.err.println("ERROR: module already defined: " + msym);
214176669Spiso//                } else {
215176669Spiso//                    syms.modules.put(msym.name, msym);
216176669Spiso//                }
217176669Spiso            }
218176669Spiso
219223080Sae            msym.classLocation = StandardLocation.CLASS_OUTPUT;
220223080Sae            return msym;
221176669Spiso
222176669Spiso        } catch (IOException e) {
223176669Spiso            throw new Error(e); // FIXME
224176669Spiso        }
225200909Sluigi    }
226200909Sluigi
227200909Sluigi    private JavaFileObject getModuleInfoFromLocation(Location location, Kind kind) throws IOException {
228200909Sluigi        if (!fileManager.hasLocation(location))
229200909Sluigi            return null;
230176669Spiso
231176669Spiso        return fileManager.getJavaFileForInput(location,
232200909Sluigi                                               names.module_info.toString(),
233176669Spiso                                               kind);
234176669Spiso    }
235200909Sluigi
236200909Sluigi    private List<ModuleSymbol> scanModulePath(ModuleSymbol toFind) {
237200909Sluigi        ListBuffer<ModuleSymbol> results = new ListBuffer<>();
238200909Sluigi        Map<Name, Location> namesInSet = new HashMap<>();
239176669Spiso        while (moduleLocationIterator.hasNext()) {
240200909Sluigi            Set<Location> locns = (moduleLocationIterator.next());
241200909Sluigi            namesInSet.clear();
242200909Sluigi            for (Location l: locns) {
243200909Sluigi                try {
244200909Sluigi                    Name n = names.fromString(fileManager.inferModuleName(l));
245200909Sluigi                    if (namesInSet.put(n, l) == null) {
246176669Spiso                        ModuleSymbol msym = syms.enterModule(n);
247200909Sluigi                        if (msym.sourceLocation != null || msym.classLocation != null) {
248200909Sluigi                            // module has already been found, so ignore this instance
249200909Sluigi                            continue;
250200909Sluigi                        }
251200909Sluigi                        if (moduleLocationIterator.outer == StandardLocation.MODULE_SOURCE_PATH) {
252200909Sluigi                            msym.sourceLocation = l;
253200909Sluigi                            if (fileManager.hasLocation(StandardLocation.CLASS_OUTPUT)) {
254200909Sluigi                                msym.classLocation = fileManager.getModuleLocation(StandardLocation.CLASS_OUTPUT, msym.name.toString());
255200909Sluigi                            }
256200909Sluigi                        } else {
257200909Sluigi                            msym.classLocation = l;
258200909Sluigi                        }
259200909Sluigi                        if (moduleLocationIterator.outer == StandardLocation.SYSTEM_MODULES ||
260176669Spiso                            moduleLocationIterator.outer == StandardLocation.UPGRADE_MODULE_PATH) {
261176669Spiso                            msym.flags_field |= Flags.SYSTEM_MODULE;
262176669Spiso                        }
263176669Spiso                        if (toFind == msym || toFind == null) {
264200909Sluigi                            // Note: cannot return msym directly, because we must finish
265200909Sluigi                            // processing this set first
266176669Spiso                            results.add(msym);
267176669Spiso                        }
268176669Spiso                    } else {
269222806Sae                        log.error(Errors.DuplicateModuleOnPath(
270223080Sae                                getDescription(moduleLocationIterator.outer), n));
271223080Sae                    }
272223080Sae                } catch (IOException e) {
273223080Sae                    // skip location for now?  log error?
274223080Sae                }
275223080Sae            }
276223080Sae            if (toFind != null && results.nonEmpty())
277223080Sae                return results.toList();
278223080Sae        }
279223080Sae
280244569Smelifaro        return results.toList();
281223080Sae    }
282223080Sae
283223080Sae    private void findModuleInfo(ModuleSymbol msym) {
284223080Sae        try {
285223080Sae            JavaFileObject src_fo = (msym.sourceLocation == null) ? null
286223080Sae                    : fileManager.getJavaFileForInput(msym.sourceLocation,
287223080Sae                            names.module_info.toString(), Kind.SOURCE);
288223080Sae
289223080Sae            JavaFileObject class_fo = (msym.classLocation == null) ? null
290223080Sae                    : fileManager.getJavaFileForInput(msym.classLocation,
291223080Sae                            names.module_info.toString(), Kind.CLASS);
292223080Sae
293223080Sae            JavaFileObject fo = (src_fo == null) ? class_fo :
294223080Sae                    (class_fo == null) ? src_fo :
295223080Sae                    classFinder.preferredFileObject(src_fo, class_fo);
296223080Sae
297223080Sae            if (fo == null) {
298223080Sae                String moduleName = msym.sourceLocation == null && msym.classLocation != null ?
299223080Sae                    fileManager.inferModuleName(msym.classLocation) : null;
300223080Sae                if (moduleName != null) {
301223080Sae                    msym.module_info.classfile = null;
302223080Sae                    msym.flags_field |= Flags.AUTOMATIC_MODULE;
303223080Sae                } else {
304223080Sae                    msym.kind = ERR;
305223080Sae                }
306223080Sae            } else {
307222806Sae                msym.module_info.classfile = fo;
308222806Sae                msym.module_info.completer = new Symbol.Completer() {
309222806Sae                    @Override
310222806Sae                    public void complete(Symbol sym) throws CompletionFailure {
311222806Sae                        classFinder.fillIn(msym.module_info);
312222806Sae                    }
313222806Sae                    @Override
314222806Sae                    public String toString() {
315222806Sae                        return "ModuleInfoCompleter";
316222806Sae                    }
317222806Sae                };
318223080Sae            }
319176669Spiso        } catch (IOException e) {
320176669Spiso            msym.kind = ERR;
321176669Spiso        }
322176669Spiso    }
323176669Spiso
324222806Sae    Fragment getDescription(StandardLocation l) {
325222806Sae        switch (l) {
326232171Sae            case MODULE_PATH: return Fragments.LocnModule_path;
327200909Sluigi            case MODULE_SOURCE_PATH: return Fragments.LocnModule_source_path;
328176669Spiso            case SYSTEM_MODULES: return Fragments.LocnSystem_modules;
329200909Sluigi            case UPGRADE_MODULE_PATH: return Fragments.LocnUpgrade_module_path;
330200909Sluigi            default:
331200909Sluigi                throw new AssertionError();
332176669Spiso        }
333176669Spiso    }
334200909Sluigi
335176669Spiso}
336200909Sluigi