zoukankan      html  css  js  c++  java
  • (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)

  • 相关阅读:
    228. Summary Ranges
    227. Basic Calculator II
    224. Basic Calculator
    222. Count Complete Tree Nodes
    223. Rectangle Area
    221. Maximal Square
    220. Contains Duplicate III
    219. Contains Duplicate II
    217. Contains Duplicate
    Java编程思想 4th 第4章 控制执行流程
  • 原文地址:https://www.cnblogs.com/Johness/p/3525032.html
Copyright © 2011-2022 走看看