javac 概述
javac 是jdk bin目录下的一个脚本。 用于编译 java程序的源代码,但是 其实现的本质 是基于 jdk 标准类库中的 javac类库实现,所以java的编译器实质上是一个 java程序。
javac脚本 仅是一个便于启动以及传递参数的脚本文件,其内部依旧运行了 java程序。
javac 又被称作前端编译器,仅负责 源代码 与 字节码之间的转换,而在jvm内部 还存在 一个后置编译器,根据热点探测技术 可以将最有价值的 字节码转换为 机器码执行从而提升java程序的运行效率。
javac 的意义就在于 将源码编译为字节码,同时 做一些 词法,语法,语义上的检查,最后生成可供jvm运行的字节码文件。
javac 源码
在 lib 中的 tools jar 包中 sun.tools.javac; 包下管理者 java前端编译器 的class文件。 Main 类 中的 main 方法的执行 是javac程序的执行入口。
1 public static void main(String args[]) 2 {
//将标准错误流获取 3 PrintStream printstream = System.err; 4 if (Boolean.getBoolean("javac.pipe.output")) 5 printstream = System.out;
//创建 编译器对象 6 Main main1 = new Main(printstream, "javac");
//调用编译器的 compile方法进行 编译 并接受 args 为参数,该参数就是 javac 后面携带的参数 7 System.exit(main1.compile(args) ? 0 : main1.exitStatus); 8 }
compile方法的 编译过程 概括性的分析:
1.解析与填充符号表
解析: 对java源代码的字节流进行读取解析,进行两个大致的步骤,词法解析以及语法解析
词法解析: 识别 java源码中存在的表达语义的逻辑单元,列如 关键字 变量名 参数名 每一个逻辑单元 称为 标量。
语法解析:将各个独立的 标量按照java语法规范形成 java语法数,语法树的每一个节点代表一个操作,运算,或者 方法调用。
填充符号表: 解析后的语法树最顶级的节点将被用来填充在符号表中,符号表存储着各个语法树的最顶级节点,填充后的符号表最终形成 待处理表。
符号表就是一个遵从java语法的结构规范,用于组织语法树的逻辑顺序。
2.插入式注解处理器
jdk1.5后引入注解功能,注解是一种应用字节码 属性中类的元数据进行操作的一种编程机制。
处理表形成后 会自动检测是否有注解器需要执行,若有则执行注解处理器。注解处理器实现了在可插入式的编译期改变编译过程的功能。
其本质就是 再次修改 处理表中的语法树。 一旦语法树被修改,则将再次进行 词法,语法分析并填充符号表的过程。
3.语义分析并生成字节码
语义分析: 再次对语法树中的节点进行校验。对数据类型以及控制逻辑进行检测。
标量检测: 检验关键字是否使用正确,类型转换是否正确等。
数据与控制流分析: 对控制流程的逻辑进行校验。
语法糖解析: 编程语言为了 增加代码的可读性,以及减少编程出错率,提供了一些并不影响程序运行期仅在编译期有效的编程机制。
java语言中语法糖 有 泛型,拆箱与装箱,foreach循环,可变参数,switch,枚举等,在编译期将转换为字节码遵守的规范形式。
泛型使用类型擦出,拆装箱调用了valueOf与xxValue方法,foreach是迭代器 可变参数是数组,switch本质是 if else 的嵌套。
字节码替换: 在生成类的字节码之时,编译器后做一些默认性质的操作,当没有显示声明的构造器,则会创建默认的无参构造器,构造器分为 实例构造器与类构造器
在字节码层面 类构造器 是指多个static代码块中的语句 收敛生成的<cinit>指令。而构造代码块与显示的构造器将收敛生成实例构造器。
同时还会将 String类型的 +与+= 操作,默认替换为 对 StringBuffer或 StrignBudiuer的操作。
最后生成字节码。
代码如下:
1 public synchronized boolean compile(String as[]) 2 { 3 String s = null; 4 String s1 = null; 5 String s2 = null; 6 String s3 = null; 7 boolean flag = false; 8 String s4 = null; 9 short word0 = 45; 10 short word1 = 3; 11 File file = null; 12 File file1 = null; 13 String s5 = "-Xjcov"; 14 String s6 = "-Xjcov:file="; 15 int i = 0x41004; 16 long l = System.currentTimeMillis(); 17 Vector vector = new Vector(); 18 boolean flag1 = false; 19 Object obj = null; 20 String s7 = null; 21 String s8 = null; 22 String s9 = null; 23 exitStatus = 0; 24 try 25 { 26 as = CommandLine.parse(as); 27 } 28 catch (FileNotFoundException filenotfoundexception) 29 { 30 error("javac.err.cant.read", filenotfoundexception.getMessage()); 31 System.exit(1); 32 } 33 catch (IOException ioexception) 34 { 35 ioexception.printStackTrace(); 36 System.exit(1); 37 } 38 label0: 39 for (int j = 0; j < as.length; j++) 40 { 41 if (as[j].equals("-g")) 42 { 43 if (s8 != null && !s8.equals("-g")) 44 error("main.conflicting.options", s8, "-g"); 45 s8 = "-g"; 46 i |= 0x1000; 47 i |= 0x2000; 48 i |= 0x40000; 49 continue; 50 } 51 if (as[j].equals("-g:none")) 52 { 53 if (s8 != null && !s8.equals("-g:none")) 54 error("main.conflicting.options", s8, "-g:none"); 55 s8 = "-g:none"; 56 i &= 0xffffefff; 57 i &= 0xffffdfff; 58 i &= 0xfffbffff; 59 continue; 60 } 61 if (as[j].startsWith("-g:")) 62 { 63 if (s8 != null && !s8.equals(as[j])) 64 error("main.conflicting.options", s8, as[j]); 65 s8 = as[j]; 66 String s10 = as[j].substring("-g:".length()); 67 i &= 0xffffefff; 68 i &= 0xffffdfff; 69 i &= 0xfffbffff; 70 do 71 { 72 do 73 { 74 if (s10.startsWith("lines")) 75 { 76 i |= 0x1000; 77 s10 = s10.substring("lines".length()); 78 } else 79 if (s10.startsWith("vars")) 80 { 81 i |= 0x2000; 82 s10 = s10.substring("vars".length()); 83 } else 84 if (s10.startsWith("source")) 85 { 86 i |= 0x40000; 87 s10 = s10.substring("source".length()); 88 } else 89 { 90 error("main.bad.debug.option", as[j]); 91 usage_error(); 92 return false; 93 } 94 if (s10.length() == 0) 95 continue label0; 96 } while (!s10.startsWith(",")); 97 s10 = s10.substring(",".length()); 98 } while (true); 99 } 100 if (as[j].equals("-O")) 101 { 102 if (s9 != null && !s9.equals("-O")) 103 error("main.conflicting.options", s9, "-O"); 104 s9 = "-O"; 105 continue; 106 } 107 if (as[j].equals("-nowarn")) 108 { 109 i &= -5; 110 continue; 111 } 112 if (as[j].equals("-deprecation")) 113 { 114 i |= 0x200; 115 continue; 116 } 117 if (as[j].equals("-verbose")) 118 { 119 i |= 1; 120 continue; 121 } 122 if (as[j].equals("-nowrite")) 123 { 124 flag1 = true; 125 continue; 126 } 127 if (as[j].equals("-classpath")) 128 { 129 if (j + 1 < as.length) 130 { 131 if (s1 != null) 132 error("main.option.already.seen", "-classpath"); 133 s1 = as[++j]; 134 } else 135 { 136 error("main.option.requires.argument", "-classpath"); 137 usage_error(); 138 return false; 139 } 140 continue; 141 } 142 if (as[j].equals("-sourcepath")) 143 { 144 if (j + 1 < as.length) 145 { 146 if (s != null) 147 error("main.option.already.seen", "-sourcepath"); 148 s = as[++j]; 149 } else 150 { 151 error("main.option.requires.argument", "-sourcepath"); 152 usage_error(); 153 return false; 154 } 155 continue; 156 } 157 if (as[j].equals("-sysclasspath")) 158 { 159 if (j + 1 < as.length) 160 { 161 if (s2 != null) 162 error("main.option.already.seen", "-sysclasspath"); 163 s2 = as[++j]; 164 } else 165 { 166 error("main.option.requires.argument", "-sysclasspath"); 167 usage_error(); 168 return false; 169 } 170 continue; 171 } 172 if (as[j].equals("-bootclasspath")) 173 { 174 if (j + 1 < as.length) 175 { 176 if (s2 != null) 177 error("main.option.already.seen", "-bootclasspath"); 178 s2 = as[++j]; 179 } else 180 { 181 error("main.option.requires.argument", "-bootclasspath"); 182 usage_error(); 183 return false; 184 } 185 continue; 186 } 187 if (as[j].equals("-extdirs")) 188 { 189 if (j + 1 < as.length) 190 { 191 if (s3 != null) 192 error("main.option.already.seen", "-extdirs"); 193 s3 = as[++j]; 194 } else 195 { 196 error("main.option.requires.argument", "-extdirs"); 197 usage_error(); 198 return false; 199 } 200 continue; 201 } 202 if (as[j].equals("-encoding")) 203 { 204 if (j + 1 < as.length) 205 { 206 if (s7 != null) 207 error("main.option.already.seen", "-encoding"); 208 s7 = as[++j]; 209 } else 210 { 211 error("main.option.requires.argument", "-encoding"); 212 usage_error(); 213 return false; 214 } 215 continue; 216 } 217 if (as[j].equals("-target")) 218 { 219 if (j + 1 < as.length) 220 { 221 if (s4 != null) 222 error("main.option.already.seen", "-target"); 223 s4 = as[++j]; 224 int k = 0; 225 do 226 { 227 if (k >= releases.length) 228 break; 229 if (releases[k].equals(s4)) 230 { 231 word0 = majorVersions[k]; 232 word1 = minorVersions[k]; 233 break; 234 } 235 k++; 236 } while (true); 237 if (k == releases.length) 238 { 239 error("main.unknown.release", s4); 240 usage_error(); 241 return false; 242 } 243 } else 244 { 245 error("main.option.requires.argument", "-target"); 246 usage_error(); 247 return false; 248 } 249 continue; 250 } 251 if (as[j].equals("-d")) 252 { 253 if (j + 1 < as.length) 254 { 255 if (file != null) 256 error("main.option.already.seen", "-d"); 257 file = new File(as[++j]); 258 if (!file.exists()) 259 { 260 error("main.no.such.directory", file.getPath()); 261 usage_error(); 262 return false; 263 } 264 } else 265 { 266 error("main.option.requires.argument", "-d"); 267 usage_error(); 268 return false; 269 } 270 continue; 271 } 272 if (as[j].equals(s5)) 273 { 274 i |= 0x40; 275 i &= 0xffffbfff; 276 i &= 0xffff7fff; 277 continue; 278 } 279 if (as[j].startsWith(s6) && as[j].length() > s6.length()) 280 { 281 file1 = new File(as[j].substring(s6.length())); 282 i &= 0xffffbfff; 283 i &= 0xffff7fff; 284 i |= 0x40; 285 i |= 0x80; 286 continue; 287 } 288 if (as[j].equals("-XO")) 289 { 290 if (s9 != null && !s9.equals("-XO")) 291 error("main.conflicting.options", s9, "-XO"); 292 s9 = "-XO"; 293 i |= 0x4000; 294 continue; 295 } 296 if (as[j].equals("-Xinterclass")) 297 { 298 if (s9 != null && !s9.equals("-Xinterclass")) 299 error("main.conflicting.options", s9, "-Xinterclass"); 300 s9 = "-Xinterclass"; 301 i |= 0x4000; 302 i |= 0x8000; 303 i |= 0x20; 304 continue; 305 } 306 if (as[j].equals("-Xdepend")) 307 { 308 i |= 0x20; 309 continue; 310 } 311 if (as[j].equals("-Xdebug")) 312 { 313 i |= 2; 314 continue; 315 } 316 if (as[j].equals("-xdepend") || as[j].equals("-Xjws")) 317 { 318 i |= 0x400; 319 if (out == System.err) 320 out = System.out; 321 continue; 322 } 323 if (as[j].equals("-Xstrictdefault")) 324 { 325 i |= 0x20000; 326 continue; 327 } 328 if (as[j].equals("-Xverbosepath")) 329 { 330 flag = true; 331 continue; 332 } 333 if (as[j].equals("-Xstdout")) 334 { 335 out = System.out; 336 continue; 337 } 338 if (as[j].equals("-X")) 339 { 340 error("main.unsupported.usage"); 341 return false; 342 } 343 if (as[j].equals("-Xversion1.2")) 344 { 345 i |= 0x800; 346 continue; 347 } 348 if (as[j].endsWith(".java")) 349 { 350 vector.addElement(as[j]); 351 } else 352 { 353 error("main.no.such.option", as[j]); 354 usage_error(); 355 return false; 356 } 357 } 358 359 if (vector.size() == 0 || exitStatus == 2) 360 { 361 usage_error(); 362 return false; 363 } 364 BatchEnvironment batchenvironment = BatchEnvironment.create(out, s, s1, s2, s3); 365 if (flag) 366 output(getText("main.path.msg", batchenvironment.sourcePath.toString(), batchenvironment.binaryPath.toString())); 367 batchenvironment.flags |= i; 368 batchenvironment.majorVersion = word0; 369 batchenvironment.minorVersion = word1; 370 batchenvironment.covFile = file1; 371 batchenvironment.setCharacterEncoding(s7); 372 String s11 = getText("main.no.memory"); 373 String s12 = getText("main.stack.overflow"); 374 batchenvironment.error(0L, "warn.class.is.deprecated", "sun.tools.javac.Main"); 375 try 376 { 377 for (Enumeration enumeration = vector.elements(); enumeration.hasMoreElements();) 378 { 379 File file2 = new File((String)enumeration.nextElement()); 380 try 381 { 382 batchenvironment.parseFile(new ClassFile(file2)); 383 } 384 catch (FileNotFoundException filenotfoundexception1) 385 { 386 batchenvironment.error(0L, "cant.read", file2.getPath()); 387 exitStatus = 2; 388 } 389 } 390 391 Object obj1 = batchenvironment.getClasses(); 392 do 393 { 394 if (!((Enumeration) (obj1)).hasMoreElements()) 395 break; 396 ClassDeclaration classdeclaration = (ClassDeclaration)((Enumeration) (obj1)).nextElement(); 397 if (classdeclaration.getStatus() == 4 && !classdeclaration.getClassDefinition().isLocal()) 398 try 399 { 400 classdeclaration.getClassDefinition(batchenvironment); 401 } 402 catch (ClassNotFound classnotfound) { } 403 } while (true); 404 obj1 = new ByteArrayOutputStream(4096); 405 boolean flag2; 406 do 407 { 408 flag2 = true; 409 batchenvironment.flushErrors(); 410 Enumeration enumeration1 = batchenvironment.getClasses(); 411 do 412 { 413 if (!enumeration1.hasMoreElements()) 414 break; 415 ClassDeclaration classdeclaration1 = (ClassDeclaration)enumeration1.nextElement(); 416 switch (classdeclaration1.getStatus()) 417 { 418 case 1: // '\001' 419 case 2: // '\002' 420 default: 421 break; 422 423 case 0: // '\0' 424 if (!batchenvironment.dependencies()) 425 continue; 426 // fall through 427 428 case 3: // '\003' 429 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (SOURCE): loading, ").append(classdeclaration1).toString()); 430 flag2 = false; 431 batchenvironment.loadDefinition(classdeclaration1); 432 if (classdeclaration1.getStatus() != 4) 433 { 434 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (SOURCE): not parsed, ").append(classdeclaration1).toString()); 435 continue; 436 } 437 // fall through 438 439 case 4: // '\004' 440 if (classdeclaration1.getClassDefinition().isInsideLocal()) 441 { 442 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (PARSED): skipping local class, ").append(classdeclaration1).toString()); 443 continue; 444 } 445 flag2 = false; 446 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (PARSED): checking, ").append(classdeclaration1).toString()); 447 SourceClass sourceclass = (SourceClass)classdeclaration1.getClassDefinition(batchenvironment); 448 sourceclass.check(batchenvironment); 449 classdeclaration1.setDefinition(sourceclass, 5); 450 // fall through 451 452 case 5: // '\005' 453 SourceClass sourceclass1 = (SourceClass)classdeclaration1.getClassDefinition(batchenvironment); 454 if (sourceclass1.getError()) 455 { 456 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (CHECKED): bailing out on error, ").append(classdeclaration1).toString()); 457 classdeclaration1.setDefinition(sourceclass1, 6); 458 continue; 459 } 460 flag2 = false; 461 ((ByteArrayOutputStream) (obj1)).reset(); 462 batchenvironment.dtEvent((new StringBuilder()).append("Main.compile (CHECKED): compiling, ").append(classdeclaration1).toString()); 463 sourceclass1.compile(((OutputStream) (obj1))); 464 classdeclaration1.setDefinition(sourceclass1, 6); 465 sourceclass1.cleanup(batchenvironment); 466 if (sourceclass1.getNestError() || flag1) 467 continue; 468 String s14 = classdeclaration1.getName().getQualifier().toString().replace('.', File.separatorChar); 469 String s15 = (new StringBuilder()).append(classdeclaration1.getName().getFlatName().toString().replace('.', '$')).append(".class").toString(); 470 File file3; 471 if (file != null) 472 { 473 if (s14.length() > 0) 474 { 475 file3 = new File(file, s14); 476 if (!file3.exists()) 477 file3.mkdirs(); 478 file3 = new File(file3, s15); 479 } else 480 { 481 file3 = new File(file, s15); 482 } 483 } else 484 { 485 ClassFile classfile = (ClassFile)sourceclass1.getSource(); 486 if (classfile.isZipped()) 487 { 488 batchenvironment.error(0L, "cant.write", classfile.getPath()); 489 exitStatus = 2; 490 continue; 491 } 492 file3 = new File(classfile.getPath()); 493 file3 = new File(file3.getParent(), s15); 494 } 495 try 496 { 497 FileOutputStream fileoutputstream = new FileOutputStream(file3.getPath()); 498 ((ByteArrayOutputStream) (obj1)).writeTo(fileoutputstream); 499 fileoutputstream.close(); 500 if (batchenvironment.verbose()) 501 output(getText("main.wrote", file3.getPath())); 502 } 503 catch (IOException ioexception1) 504 { 505 batchenvironment.error(0L, "cant.write", file3.getPath()); 506 exitStatus = 2; 507 } 508 if (batchenvironment.print_dependencies()) 509 sourceclass1.printClassDependencies(batchenvironment); 510 break; 511 } 512 } while (true); 513 } while (!flag2); 514 } 515 catch (OutOfMemoryError outofmemoryerror) 516 { 517 batchenvironment.output(s11); 518 exitStatus = 3; 519 return false; 520 } 521 catch (StackOverflowError stackoverflowerror) 522 { 523 batchenvironment.output(s12); 524 exitStatus = 3; 525 return false; 526 } 527 catch (Error error1) 528 { 529 if (batchenvironment.nerrors == 0 || batchenvironment.dump()) 530 { 531 error1.printStackTrace(); 532 batchenvironment.error(0L, "fatal.error"); 533 exitStatus = 4; 534 } 535 } 536 catch (Exception exception) 537 { 538 if (batchenvironment.nerrors == 0 || batchenvironment.dump()) 539 { 540 exception.printStackTrace(); 541 batchenvironment.error(0L, "fatal.exception"); 542 exitStatus = 4; 543 } 544 } 545 int i1 = batchenvironment.deprecationFiles.size(); 546 if (i1 > 0 && batchenvironment.warnings()) 547 { 548 int j1 = batchenvironment.ndeprecations; 549 Object obj2 = batchenvironment.deprecationFiles.elementAt(0); 550 if (batchenvironment.deprecation()) 551 { 552 if (i1 > 1) 553 batchenvironment.error(0L, "warn.note.deprecations", new Integer(i1), new Integer(j1)); 554 else 555 batchenvironment.error(0L, "warn.note.1deprecation", obj2, new Integer(j1)); 556 } else 557 if (i1 > 1) 558 batchenvironment.error(0L, "warn.note.deprecations.silent", new Integer(i1), new Integer(j1)); 559 else 560 batchenvironment.error(0L, "warn.note.1deprecation.silent", obj2, new Integer(j1)); 561 } 562 batchenvironment.flushErrors(); 563 batchenvironment.shutdown(); 564 boolean flag3 = true; 565 if (batchenvironment.nerrors > 0) 566 { 567 String s13 = ""; 568 if (batchenvironment.nerrors > 1) 569 s13 = getText("main.errors", batchenvironment.nerrors); 570 else 571 s13 = getText("main.1error"); 572 if (batchenvironment.nwarnings > 0) 573 if (batchenvironment.nwarnings > 1) 574 s13 = (new StringBuilder()).append(s13).append(", ").append(getText("main.warnings", batchenvironment.nwarnings)).toString(); 575 else 576 s13 = (new StringBuilder()).append(s13).append(", ").append(getText("main.1warning")).toString(); 577 output(s13); 578 if (exitStatus == 0) 579 exitStatus = 1; 580 flag3 = false; 581 } else 582 if (batchenvironment.nwarnings > 0) 583 if (batchenvironment.nwarnings > 1) 584 output(getText("main.warnings", batchenvironment.nwarnings)); 585 else 586 output(getText("main.1warning")); 587 if (batchenvironment.covdata()) 588 { 589 Assembler assembler = new Assembler(); 590 assembler.GenJCov(batchenvironment); 591 } 592 if (batchenvironment.verbose()) 593 { 594 l = System.currentTimeMillis() - l; 595 output(getText("main.done_in", Long.toString(l))); 596 } 597 return flag3; 598 }