1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24package jdk.tools.jaotc;
25
26import java.io.BufferedReader;
27import java.io.FileNotFoundException;
28import java.io.FileReader;
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.HashSet;
32import java.util.List;
33import java.util.Set;
34
35import jdk.tools.jaotc.collect.ClassSearch;
36import jdk.tools.jaotc.collect.FileSupport;
37import jdk.tools.jaotc.collect.classname.ClassNameSourceProvider;
38import jdk.tools.jaotc.collect.directory.DirectorySourceProvider;
39import jdk.tools.jaotc.collect.jar.JarSourceProvider;
40import jdk.tools.jaotc.collect.module.ModuleSourceProvider;
41import jdk.vm.ci.meta.MetaAccessProvider;
42import jdk.vm.ci.meta.ResolvedJavaMethod;
43import jdk.vm.ci.meta.ResolvedJavaType;
44
45final class Collector {
46
47    private final Main main;
48
49    Collector(Main main) {
50        this.main = main;
51    }
52
53    Set<Class<?>> collectClassesToCompile() {
54        Set<Class<?>> classesToCompile = new HashSet<>();
55        FileSupport fileSupport = new FileSupport();
56        ClassSearch lookup = new ClassSearch();
57        lookup.addProvider(new ModuleSourceProvider());
58        lookup.addProvider(new ClassNameSourceProvider(fileSupport));
59        lookup.addProvider(new JarSourceProvider());
60        lookup.addProvider(new DirectorySourceProvider(fileSupport));
61
62        List<LoadedClass> foundClasses = null;
63        try {
64            foundClasses = lookup.search(main.options.files, main.options.searchPath);
65        } catch (InternalError e) {
66            main.printer.reportError(e);
67            return null;
68        }
69
70        for (LoadedClass loadedClass : foundClasses) {
71            classesToCompile.add(loadedClass.getLoadedClass());
72        }
73        return classesToCompile;
74    }
75
76    private void addMethods(AOTCompiledClass aotClass, ResolvedJavaMethod[] methods, CompilationSpec compilationRestrictions) {
77        for (ResolvedJavaMethod m : methods) {
78            addMethod(aotClass, m, compilationRestrictions);
79        }
80    }
81
82    private void addMethod(AOTCompiledClass aotClass, ResolvedJavaMethod method, CompilationSpec compilationRestrictions) {
83        // Don't compile native or abstract methods.
84        if (!method.hasBytecodes()) {
85            return;
86        }
87        if (!compilationRestrictions.shouldCompileMethod(method)) {
88            return;
89        }
90        if (!main.filters.shouldCompileMethod(method)) {
91            return;
92        }
93
94        aotClass.addMethod(method);
95        main.printer.printlnVerbose("  added " + method.getName() + method.getSignature().toMethodDescriptor());
96    }
97
98    /**
99     * Collect all method we should compile.
100     *
101     * @return array list of AOT classes which have compiled methods.
102     */
103    List<AOTCompiledClass> collectMethodsToCompile(Set<Class<?>> classesToCompile, MetaAccessProvider metaAccess) {
104        int total = 0;
105        int count = 0;
106        List<AOTCompiledClass> classes = new ArrayList<>();
107        CompilationSpec compilationRestrictions = collectSpecifiedMethods();
108
109        for (Class<?> c : classesToCompile) {
110            ResolvedJavaType resolvedJavaType = metaAccess.lookupJavaType(c);
111            if (main.filters.shouldCompileAnyMethodInClass(resolvedJavaType)) {
112                AOTCompiledClass aotClass = new AOTCompiledClass(resolvedJavaType);
113                main.printer.printlnVerbose(" Scanning " + c.getName());
114
115                // Constructors
116                try {
117                    ResolvedJavaMethod[] ctors = resolvedJavaType.getDeclaredConstructors();
118                    addMethods(aotClass, ctors, compilationRestrictions);
119                    total += ctors.length;
120                } catch (Throwable e) {
121                    // If we are running in JCK mode we ignore all exceptions.
122                    if (main.options.ignoreClassLoadingErrors) {
123                        main.printer.printError(c.getName() + ": " + e);
124                    } else {
125                        throw new InternalError(e);
126                    }
127                }
128
129                // Methods
130                try {
131                    ResolvedJavaMethod[] methods = resolvedJavaType.getDeclaredMethods();
132                    addMethods(aotClass, methods, compilationRestrictions);
133                    total += methods.length;
134                } catch (Throwable e) {
135                    // If we are running in JCK mode we ignore all exceptions.
136                    if (main.options.ignoreClassLoadingErrors) {
137                        main.printer.printError(c.getName() + ": " + e);
138                    } else {
139                        throw new InternalError(e);
140                    }
141                }
142
143                // Class initializer
144                try {
145                    ResolvedJavaMethod clinit = resolvedJavaType.getClassInitializer();
146                    if (clinit != null) {
147                        addMethod(aotClass, clinit, compilationRestrictions);
148                        total++;
149                    }
150                } catch (Throwable e) {
151                    // If we are running in JCK mode we ignore all exceptions.
152                    if (main.options.ignoreClassLoadingErrors) {
153                        main.printer.printError(c.getName() + ": " + e);
154                    } else {
155                        throw new InternalError(e);
156                    }
157                }
158
159                // Found any methods to compile? Add the class.
160                if (aotClass.hasMethods()) {
161                    classes.add(aotClass);
162                    count += aotClass.getMethodCount();
163                }
164            }
165        }
166        main.printer.printInfo(total + " methods total, " + count + " methods to compile");
167        return classes;
168    }
169
170    /**
171     * If a file with compilation limitations is specified using flag --compile-commands, read the
172     * file's contents and collect the restrictions.
173     */
174    private CompilationSpec collectSpecifiedMethods() {
175        CompilationSpec compilationRestrictions = new CompilationSpec();
176        String methodListFileName = main.options.methodList;
177
178        if (methodListFileName != null && !methodListFileName.equals("")) {
179            try {
180                FileReader methListFile = new FileReader(methodListFileName);
181                BufferedReader readBuf = new BufferedReader(methListFile);
182                String line = null;
183                while ((line = readBuf.readLine()) != null) {
184                    String trimmedLine = line.trim();
185                    if (!trimmedLine.startsWith("#")) {
186                        String[] components = trimmedLine.split(" ");
187                        if (components.length == 2) {
188                            String directive = components[0];
189                            String pattern = components[1];
190                            switch (directive) {
191                                case "compileOnly":
192                                    compilationRestrictions.addCompileOnlyPattern(pattern);
193                                    break;
194                                case "exclude":
195                                    compilationRestrictions.addExcludePattern(pattern);
196                                    break;
197                                default:
198                                    System.out.println("Unrecognized command " + directive + ". Ignoring\n\t" + line + "\n encountered in " + methodListFileName);
199                            }
200                        } else {
201                            if (!trimmedLine.equals("")) {
202                                System.out.println("Ignoring malformed line:\n\t " + line + "\n");
203                            }
204                        }
205                    }
206                }
207                readBuf.close();
208            } catch (FileNotFoundException e) {
209                throw new InternalError("Unable to open method list file: " + methodListFileName, e);
210            } catch (IOException e) {
211                throw new InternalError("Unable to read method list file: " + methodListFileName, e);
212            }
213        }
214
215        return compilationRestrictions;
216    }
217
218}
219