(ecj)Eclipse的Java编译器分析之一——ecj介绍

  Java是一个开放的平台,对于除发布编译器/解释器/基础类库之外,该语言的负责机构更多的是制定一系列标准,任何符合标准的厂商产品均可用于市场投放。甚至包括其编译器及解释器。

  (比如Hibernate提供了JPA实现;Tomcat实现了Java EE服务器标准,其Servlet容器通过了Java认证;各数据库或中间件厂商也根据JDBC接口开发驱动。说白了,Java基本就是都提供接口,然后让厂商开发实现,因此有时候我会骂,边骂边编码!)

  GCC有java编译器,可以看看。

  我们主要主要介绍Eclipse自己开发和使用的针对Java的编译器:(ecj) the Eclipse Compiler for Java。Eclipse没有使用JDK自带的编译器,而是自己开发的,ecj也通过了java的验证。

 

  除了Eclipse之外,Tomcat也用到了Ecj,用于动态编译jsp文件。我们安装Tomcat后可在lib文件夹下找到ecj:

  

  现在问题来了:怎么取得ecj源码呢?

  别急,我们从tomcat源码中查看一下:

  

  

  虽然我不熟ant,但我也能知道,Tomcat6.0.37中使用的ecj下载路径是:

  http://archive.eclipse.org/eclipse/downloads/drops4/R-4.2.2-201302041200/ecj-4.2.2.jar

  这个还是class文件,刚开始我也没辙,不过后来我灵机一动:在ecj后面加个src竟然成了!-_-

  http://archive.eclipse.org/eclipse/downloads/drops4/R-4.2.2-201302041200/ecjsrc-4.2.2.jar

  下面是我下载好后倒入项目文件后截图:

  

  

  这个文件报错,不过可以把他删除了看,我先没有删除,因为这个文件是ecj与ant的桥梁。从源码可以看出这个JDTCompilerAdapter是继承自ant的DefaultCompilerAdapter,用于ant的编译器适配器。个人感觉ecj从代码(技术)上并没有耦合任何一个调用者,这里的ant也只是一个适配器,你删除或者留着没有任何影响。Tomcat里也没有使用ant。

  我从这里主要是想看看高层怎么调用ecj来编译代码,我们看看关键代码:

  

 1 private static String compilerClass = "org.eclipse.jdt.internal.compiler.batch.Main"; //$NON-NLS-1$
 2 
 3 /**
 4      * Performs a compile using the JDT batch compiler
 5      * @throws BuildException if anything wrong happen during the compilation
 6      * @return boolean true if the compilation is ok, false otherwise
 7      */
 8     public boolean execute() throws BuildException {
 9         this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.info.usingJDTCompiler"), Project.MSG_VERBOSE); //$NON-NLS-1$
10         Commandline cmd = setupJavacCommand();
11 
12         try {
13             Class c = Class.forName(compilerClass);
14             Constructor batchCompilerConstructor = 
15                     c.getConstructor(new Class[] { 
16                             PrintWriter.class, 
17                             PrintWriter.class, 
18                             Boolean.TYPE, 
19                             Map.class});
20             Object batchCompilerInstance = 
21                     batchCompilerConstructor.newInstance(new Object[] {
22                             new PrintWriter(System.out), 
23                             new PrintWriter(System.err), 
24                             Boolean.TRUE, 
25                             this.customDefaultOptions});
26             Method compile = 
27                     c.getMethod("compile", new Class[] {String[].class}); //$NON-NLS-1$
28             Object result = 
29                     compile.invoke(batchCompilerInstance, new Object[] { 
30                             cmd.getArguments()});
31             final boolean resultValue = ((Boolean) result).booleanValue();
32             if (!resultValue && this.logFileName != null) {
33                 this.attributes.log(AntAdapterMessages.getString("ant.jdtadapter.error.compilationFailed", this.logFileName)); //$NON-NLS-1$
34             }
35             return resultValue;
36         } catch (ClassNotFoundException cnfe) {
37             throw new BuildException(AntAdapterMessages.getString("ant.jdtadapter.error.cannotFindJDTCompiler")); //$NON-NLS-1$
38         } catch (Exception ex) {
39             throw new BuildException(ex);
40         }
41     }

  我把代码换了下行,大家看13和26行,可以看出这里使用了

  org.eclipse.jdt.internal.compiler.batch.Main#compile(String[])方法来进行编译,我们可以稍微看看:

  从源码上来看1664是配置,1684可能是编译,不过我们先不细看。

  我们再看看Tomcat怎么使用ecj的,我们查看org.apache.jasper.compiler.JDTCompiler源码(我贴出了源码,不过有点长):

  

  1 /*
  2  * Licensed to the Apache Software Foundation (ASF) under one or more
  3  * contributor license agreements.  See the NOTICE file distributed with
  4  * this work for additional information regarding copyright ownership.
  5  * The ASF licenses this file to You under the Apache License, Version 2.0
  6  * (the "License"); you may not use this file except in compliance with
  7  * the License.  You may obtain a copy of the License at
  8  * 
  9  *      http://www.apache.org/licenses/LICENSE-2.0
 10  * 
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17 
 18 package org.apache.jasper.compiler;
 19 
 20 import java.io.BufferedOutputStream;
 21 import java.io.BufferedReader;
 22 import java.io.ByteArrayOutputStream;
 23 import java.io.File;
 24 import java.io.FileInputStream;
 25 import java.io.FileNotFoundException;
 26 import java.io.FileOutputStream;
 27 import java.io.IOException;
 28 import java.io.InputStream;
 29 import java.io.InputStreamReader;
 30 import java.io.Reader;
 31 import java.util.ArrayList;
 32 import java.util.HashMap;
 33 import java.util.Locale;
 34 import java.util.Map;
 35 import java.util.StringTokenizer;
 36 
 37 import org.apache.jasper.JasperException;
 38 import org.eclipse.jdt.core.compiler.IProblem;
 39 import org.eclipse.jdt.internal.compiler.ClassFile;
 40 import org.eclipse.jdt.internal.compiler.CompilationResult;
 41 import org.eclipse.jdt.internal.compiler.Compiler;
 42 import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
 43 import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
 44 import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
 45 import org.eclipse.jdt.internal.compiler.IProblemFactory;
 46 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
 47 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
 48 import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
 49 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
 50 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
 51 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
 52 
 53 /**
 54  * JDT class compiler. This compiler will load source dependencies from the
 55  * context classloader, reducing dramatically disk access during 
 56  * the compilation process.
 57  *
 58  * @author Cocoon2
 59  * @author Remy Maucherat
 60  */
 61 public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
 62 
 63     
 64     /** 
 65      * Compile the servlet from .java file to .class file
 66      */
 67     protected void generateClass(String[] smap)
 68         throws FileNotFoundException, JasperException, Exception {
 69 
 70         long t1 = 0;
 71         if (log.isDebugEnabled()) {
 72             t1 = System.currentTimeMillis();
 73         }
 74         
 75         final String sourceFile = ctxt.getServletJavaFileName();
 76         final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
 77         String packageName = ctxt.getServletPackageName();
 78         final String targetClassName = 
 79             ((packageName.length() != 0) ? (packageName + ".") : "") 
 80                     + ctxt.getServletClassName();
 81         final ClassLoader classLoader = ctxt.getJspLoader();
 82         String[] fileNames = new String[] {sourceFile};
 83         String[] classNames = new String[] {targetClassName};
 84         final ArrayList problemList = new ArrayList();
 85         
 86         class CompilationUnit implements ICompilationUnit {
 87 
 88             String className;
 89             String sourceFile;
 90 
 91             CompilationUnit(String sourceFile, String className) {
 92                 this.className = className;
 93                 this.sourceFile = sourceFile;
 94             }
 95 
 96             public char[] getFileName() {
 97                 return sourceFile.toCharArray();
 98             }
 99             
100             public char[] getContents() {
101                 char[] result = null;
102                 FileInputStream is = null;
103                 try {
104                     is = new FileInputStream(sourceFile);
105                     Reader reader = 
106                         new BufferedReader(new InputStreamReader(is, ctxt.getOptions().getJavaEncoding()));
107                     if (reader != null) {
108                         char[] chars = new char[8192];
109                         StringBuffer buf = new StringBuffer();
110                         int count;
111                         while ((count = reader.read(chars, 0, 
112                                                     chars.length)) > 0) {
113                             buf.append(chars, 0, count);
114                         }
115                         result = new char[buf.length()];
116                         buf.getChars(0, result.length, result, 0);
117                     }
118                 } catch (IOException e) {
119                     log.error("Compilation error", e);
120                 } finally {
121                     if (is != null) {
122                         try {
123                             is.close();
124                         } catch (IOException exc) {
125                             // Ignore
126                         }
127                     }
128                 }
129                 return result;
130             }
131             
132             public char[] getMainTypeName() {
133                 int dot = className.lastIndexOf('.');
134                 if (dot > 0) {
135                     return className.substring(dot + 1).toCharArray();
136                 }
137                 return className.toCharArray();
138             }
139             
140             public char[][] getPackageName() {
141                 StringTokenizer izer = 
142                     new StringTokenizer(className, ".");
143                 char[][] result = new char[izer.countTokens()-1][];
144                 for (int i = 0; i < result.length; i++) {
145                     String tok = izer.nextToken();
146                     result[i] = tok.toCharArray();
147                 }
148                 return result;
149             }
150 
151             public boolean ignoreOptionalProblems() {
152                 return false;
153             }
154         }
155 
156         final INameEnvironment env = new INameEnvironment() {
157 
158                 public NameEnvironmentAnswer 
159                     findType(char[][] compoundTypeName) {
160                     String result = "";
161                     String sep = "";
162                     for (int i = 0; i < compoundTypeName.length; i++) {
163                         result += sep;
164                         result += new String(compoundTypeName[i]);
165                         sep = ".";
166                     }
167                     return findType(result);
168                 }
169 
170                 public NameEnvironmentAnswer 
171                     findType(char[] typeName, 
172                              char[][] packageName) {
173                         String result = "";
174                         String sep = "";
175                         for (int i = 0; i < packageName.length; i++) {
176                             result += sep;
177                             result += new String(packageName[i]);
178                             sep = ".";
179                         }
180                         result += sep;
181                         result += new String(typeName);
182                         return findType(result);
183                 }
184                 
185                 private NameEnvironmentAnswer findType(String className) {
186 
187                     InputStream is = null;
188                     try {
189                         if (className.equals(targetClassName)) {
190                             ICompilationUnit compilationUnit = 
191                                 new CompilationUnit(sourceFile, className);
192                             return 
193                                 new NameEnvironmentAnswer(compilationUnit, null);
194                         }
195                         String resourceName = 
196                             className.replace('.', '/') + ".class";
197                         is = classLoader.getResourceAsStream(resourceName);
198                         if (is != null) {
199                             byte[] classBytes;
200                             byte[] buf = new byte[8192];
201                             ByteArrayOutputStream baos = 
202                                 new ByteArrayOutputStream(buf.length);
203                             int count;
204                             while ((count = is.read(buf, 0, buf.length)) > 0) {
205                                 baos.write(buf, 0, count);
206                             }
207                             baos.flush();
208                             classBytes = baos.toByteArray();
209                             char[] fileName = className.toCharArray();
210                             ClassFileReader classFileReader = 
211                                 new ClassFileReader(classBytes, fileName, 
212                                                     true);
213                             return 
214                                 new NameEnvironmentAnswer(classFileReader, null);
215                         }
216                     } catch (IOException exc) {
217                         log.error("Compilation error", exc);
218                     } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
219                         log.error("Compilation error", exc);
220                     } finally {
221                         if (is != null) {
222                             try {
223                                 is.close();
224                             } catch (IOException exc) {
225                                 // Ignore
226                             }
227                         }
228                     }
229                     return null;
230                 }
231 
232                 private boolean isPackage(String result) {
233                     if (result.equals(targetClassName)) {
234                         return false;
235                     }
236                     String resourceName = result.replace('.', '/') + ".class";
237                     InputStream is = 
238                         classLoader.getResourceAsStream(resourceName);
239                     return is == null;
240                 }
241 
242                 public boolean isPackage(char[][] parentPackageName, 
243                                          char[] packageName) {
244                     String result = "";
245                     String sep = "";
246                     if (parentPackageName != null) {
247                         for (int i = 0; i < parentPackageName.length; i++) {
248                             result += sep;
249                             String str = new String(parentPackageName[i]);
250                             result += str;
251                             sep = ".";
252                         }
253                     }
254                     String str = new String(packageName);
255                     if (Character.isUpperCase(str.charAt(0))) {
256                         if (!isPackage(result)) {
257                             return false;
258                         }
259                     }
260                     result += sep;
261                     result += str;
262                     return isPackage(result);
263                 }
264 
265                 public void cleanup() {
266                 }
267 
268             };
269 
270         final IErrorHandlingPolicy policy = 
271             DefaultErrorHandlingPolicies.proceedWithAllProblems();
272 
273         final Map settings = new HashMap();
274         settings.put(CompilerOptions.OPTION_LineNumberAttribute,
275                      CompilerOptions.GENERATE);
276         settings.put(CompilerOptions.OPTION_SourceFileAttribute,
277                      CompilerOptions.GENERATE);
278         settings.put(CompilerOptions.OPTION_ReportDeprecation,
279                      CompilerOptions.IGNORE);
280         if (ctxt.getOptions().getJavaEncoding() != null) {
281             settings.put(CompilerOptions.OPTION_Encoding,
282                     ctxt.getOptions().getJavaEncoding());
283         }
284         if (ctxt.getOptions().getClassDebugInfo()) {
285             settings.put(CompilerOptions.OPTION_LocalVariableAttribute,
286                          CompilerOptions.GENERATE);
287         }
288 
289         // Source JVM
290         if(ctxt.getOptions().getCompilerSourceVM() != null) {
291             String opt = ctxt.getOptions().getCompilerSourceVM();
292             if(opt.equals("1.1")) {
293                 settings.put(CompilerOptions.OPTION_Source,
294                              CompilerOptions.VERSION_1_1);
295             } else if(opt.equals("1.2")) {
296                 settings.put(CompilerOptions.OPTION_Source,
297                              CompilerOptions.VERSION_1_2);
298             } else if(opt.equals("1.3")) { 
299                 settings.put(CompilerOptions.OPTION_Source,
300                              CompilerOptions.VERSION_1_3);
301             } else if(opt.equals("1.4")) {
302                 settings.put(CompilerOptions.OPTION_Source,
303                              CompilerOptions.VERSION_1_4);
304             } else if(opt.equals("1.5")) {
305                 settings.put(CompilerOptions.OPTION_Source,
306                              CompilerOptions.VERSION_1_5);
307             } else if(opt.equals("1.6")) {
308                 settings.put(CompilerOptions.OPTION_Source,
309                              CompilerOptions.VERSION_1_6);
310             } else if(opt.equals("1.7")) {
311                 settings.put(CompilerOptions.OPTION_Source,
312                              CompilerOptions.VERSION_1_7);
313             } else {
314                 log.warn("Unknown source VM " + opt + " ignored.");
315                 settings.put(CompilerOptions.OPTION_Source,
316                         CompilerOptions.VERSION_1_5);
317             }
318         } else {
319             // Default to 1.5
320             settings.put(CompilerOptions.OPTION_Source,
321                     CompilerOptions.VERSION_1_5);
322         }
323         
324         // Target JVM
325         if(ctxt.getOptions().getCompilerTargetVM() != null) {
326             String opt = ctxt.getOptions().getCompilerTargetVM();
327             if(opt.equals("1.1")) {
328                 settings.put(CompilerOptions.OPTION_TargetPlatform,
329                              CompilerOptions.VERSION_1_1);
330             } else if(opt.equals("1.2")) {
331                 settings.put(CompilerOptions.OPTION_TargetPlatform,
332                              CompilerOptions.VERSION_1_2);
333             } else if(opt.equals("1.3")) { 
334                 settings.put(CompilerOptions.OPTION_TargetPlatform,
335                              CompilerOptions.VERSION_1_3);
336             } else if(opt.equals("1.4")) {
337                 settings.put(CompilerOptions.OPTION_TargetPlatform,
338                              CompilerOptions.VERSION_1_4);
339             } else if(opt.equals("1.5")) {
340                 settings.put(CompilerOptions.OPTION_TargetPlatform,
341                              CompilerOptions.VERSION_1_5);
342                 settings.put(CompilerOptions.OPTION_Compliance,
343                         CompilerOptions.VERSION_1_5);
344             } else if(opt.equals("1.6")) {
345                 settings.put(CompilerOptions.OPTION_TargetPlatform,
346                              CompilerOptions.VERSION_1_6);
347                 settings.put(CompilerOptions.OPTION_Compliance,
348                         CompilerOptions.VERSION_1_6);
349             } else if(opt.equals("1.7")) {
350                 settings.put(CompilerOptions.OPTION_TargetPlatform,
351                              CompilerOptions.VERSION_1_7);
352                 settings.put(CompilerOptions.OPTION_Compliance,
353                         CompilerOptions.VERSION_1_7);
354             } else {
355                 log.warn("Unknown target VM " + opt + " ignored.");
356                 settings.put(CompilerOptions.OPTION_TargetPlatform,
357                         CompilerOptions.VERSION_1_5);
358             }
359         } else {
360             // Default to 1.5
361             settings.put(CompilerOptions.OPTION_TargetPlatform,
362                     CompilerOptions.VERSION_1_5);
363             settings.put(CompilerOptions.OPTION_Compliance,
364                     CompilerOptions.VERSION_1_5);
365         }
366 
367         final IProblemFactory problemFactory = 
368             new DefaultProblemFactory(Locale.getDefault());
369         
370         final ICompilerRequestor requestor = new ICompilerRequestor() {
371                 public void acceptResult(CompilationResult result) {
372                     try {
373                         if (result.hasProblems()) {
374                             IProblem[] problems = result.getProblems();
375                             for (int i = 0; i < problems.length; i++) {
376                                 IProblem problem = problems[i];
377                                 if (problem.isError()) {
378                                     String name = 
379                                         new String(problems[i].getOriginatingFileName());
380                                     try {
381                                         problemList.add(ErrorDispatcher.createJavacError
382                                                 (name, pageNodes, new StringBuffer(problem.getMessage()), 
383                                                         problem.getSourceLineNumber(), ctxt));
384                                     } catch (JasperException e) {
385                                         log.error("Error visiting node", e);
386                                     }
387                                 }
388                             }
389                         }
390                         if (problemList.isEmpty()) {
391                             ClassFile[] classFiles = result.getClassFiles();
392                             for (int i = 0; i < classFiles.length; i++) {
393                                 ClassFile classFile = classFiles[i];
394                                 char[][] compoundName = 
395                                     classFile.getCompoundName();
396                                 String className = "";
397                                 String sep = "";
398                                 for (int j = 0; 
399                                      j < compoundName.length; j++) {
400                                     className += sep;
401                                     className += new String(compoundName[j]);
402                                     sep = ".";
403                                 }
404                                 byte[] bytes = classFile.getBytes();
405                                 String outFile = outputDir + "/" + 
406                                     className.replace('.', '/') + ".class";
407                                 FileOutputStream fout = 
408                                     new FileOutputStream(outFile);
409                                 BufferedOutputStream bos = 
410                                     new BufferedOutputStream(fout);
411                                 bos.write(bytes);
412                                 bos.close();
413                             }
414                         }
415                     } catch (IOException exc) {
416                         log.error("Compilation error", exc);
417                     }
418                 }
419             };
420 
421         ICompilationUnit[] compilationUnits = 
422             new ICompilationUnit[classNames.length];
423         for (int i = 0; i < compilationUnits.length; i++) {
424             String className = classNames[i];
425             compilationUnits[i] = new CompilationUnit(fileNames[i], className);
426         }
427         Compiler compiler = new Compiler(env,
428                                          policy,
429                                          settings,
430                                          requestor,
431                                          problemFactory,
432                                          true);
433         compiler.compile(compilationUnits);
434 
435         if (!ctxt.keepGenerated()) {
436             File javaFile = new File(ctxt.getServletJavaFileName());
437             javaFile.delete();
438         }
439     
440         if (!problemList.isEmpty()) {
441             JavacErrorDetail[] jeds = 
442                 (JavacErrorDetail[]) problemList.toArray(new JavacErrorDetail[0]);
443             errDispatcher.javacError(jeds);
444         }
445         
446         if( log.isDebugEnabled() ) {
447             long t2=System.currentTimeMillis();
448             log.debug("Compiled " + ctxt.getServletJavaFileName() + " "
449                       + (t2-t1) + "ms");
450         }
451 
452         if (ctxt.isPrototypeMode()) {
453             return;
454         }
455 
456         // JSR45 Support
457         if (! options.isSmapSuppressed()) {
458             SmapUtil.installSmap(smap);
459         }
460         
461     }
462     
463     
464 }
TomcatAdapter

  

  从427可以知道,Tomcat使用了org.eclipse.jdt.internal.compiler.Compiler#compile(ICompilationUnit[])

  当然,在这之前使用了很多代码来进行配置。

 

  第一篇文章就到这里。

 

 欢迎您移步我们的交流群,无聊的时候大家一起打发时间:Programmer Union

 或者通过QQ与我联系:点击这里给我发消息

 (最后编辑时间2014-01-18 13:59:01)

posted @ 2014-01-18 14:00  云中双月  阅读(21993)  评论(1编辑  收藏  举报