java编译器源码解析(java编译器原理)

  本篇文章为你整理了java编译器源码解析(java编译器原理)的详细内容,包含有java编译器源代码 java编译器原理 java编译器ij 编译java源代码 java编译器源码解析,希望能帮助你了解 java编译器源码解析。

  填充符号表的核心逻辑在com.sun.tools.javac.comp.Enter类。

  在讨论填充符号表的逻辑之前,首先要明确一下,什么是符号?

  我们在java代码中,可能会声明一个类,类中有属性和方法,这些对于计算机而言,都是一种符号。

  在java编译器的实现中,定义了专门的符号类Symbol及相关的子类

  

  

  

  符号有名称,就是我们理解的类名、方法名和属性名。

  除此之外,符号还有类型,java专门设计了一套符号类型系统来标识它。

  我们举个例子

  

int a=0;

 

  我们知道,a是一个变量,但编译器认为a是一个VarSymbol,它的类型是JCPrimitiveType.

  在执行程序的时候,原生类型和引用类型有不同的处理方式。

  原生类型可能存放在常量池中,但引用类型必须存在堆中,所以在编译器在编译期间就需要标识起来。

  

  

  填充符号表分为两个阶段:

  第一:类符号填充Enter

  主方法为complete()所有类都进入其作用域,在visit类声明的时候,给相关的JCClassDecl节点的sym类型定义了一个ClassSymbol的值。

  类符号填充完成后,进入第二阶段,调用enterMember.complete方法

  第二:其他成员符号填充

  逻辑在MemberEnter类的complete方法中

  MemberEnter.visitMethodDef()给方法声明节点的符号表填充符号。

  MemberEnter.visitVarDef()给变量声明节点的符号表填充符号。

  第三:其他的符号填充在后续阶段完成

  生成符号的同时,符号也被放入相应的Scope中。

  Scope又叫作用域,它是符号Symbol的容器,它提供了使用符号名访问符号的方法。

  Scope类被实现为具有“开放寻址”和“双重哈希”的哈希表。Scope是可以嵌套的。

  二、Symbol及其核心子类

  

  

  虽然我们这一步的工作是填充符号表,但我发现并不是JCTree所有的节点都有对应的符号,查看源码发现只有这些节点是有对应符号的。

  JCCompilationUnit里持有PackageSymbol属性,

  JCClassDecl持有ClassSymbol属性,属性名sym

  JCMethodDecl持有MethodSymbol属性

  JCVariableDecl持有VarSymbol属性

  JCNewClass持有Symbol类型的属性,属性名constructor

  JCAssignOp持有Symbol类型的属性,属性名operator

  JCUnary持有Symbol类型的属性,属性名operator

  JCBinary持有Symbol类型的属性,属性名operator

  JCFieldAccess持有Symbol属性

  JCIdent持有Symbol属性

  三、填充符号表

  3.1.访问类定义

  那么以我们的理解,当我们在AST上visit到一个类定义的时候,一定会生成一个ClassSymbol对象,然后放到符号表中。

  我们看下javac中的源码实现,验证一下我们的想法,下面是Enter.visitClassDef()

  我们可以看到在visitClassDef中的第三行声明了一个类符号——ClassSymbol c,经常一系列初始化后c被赋值给tree.sym,就在这个节点对应的符号属性

  同时我们还看到了作用域有关的信息,第二行就获取了当前作用域enclScope,经过一系列操作后,把当前符号c放入了作用域enclScope中,实际是将符号放入了Scope中的一些Entry数组中。

  

public void visitClassDef(JCClassDecl tree) {

 

   Symbol owner = env.info.scope.owner;

   //获取当前作用域

   Scope enclScope = enterScope(env);

   //类符号的声明

   ClassSymbol c;

   if (owner.kind == PCK) {

   // 顶级类声明

   PackageSymbol packge = (PackageSymbol)owner;

   for (Symbol q = packge; q != null q.kind == PCK; q = q.owner)

   q.flags_field = EXISTS;

   //类符号的定义

   c = reader.enterClass(tree.name, packge);

   //将当前类放入当前包的作用域中

   packge.members().enterIfAbsent(c);

   if ((tree.mods.flags PUBLIC) != 0 !classNameMatchesFileName(c, env)) {

   log.error(tree.pos(),

   "class.public.should.be.in.file", tree.name);

   } else {

   if (!tree.name.isEmpty()

   !chk.checkUniqueClassName(tree.pos(), tree.name, enclScope)) {

   result = null;

   return;

   if (owner.kind == TYP) {

   // 内部类声明

   c = reader.enterClass(tree.name, (TypeSymbol)owner);

   if ((owner.flags_field INTERFACE) != 0) {

   tree.mods.flags = PUBLIC STATIC;

   } else {

   // 局部类声明

   c = reader.defineClass(tree.name, owner);

   c.flatname = chk.localClassName(c);

   if (!c.name.isEmpty())

   chk.checkTransparentClass(tree.pos(), c, env.info.scope);

   //将符号放入当前AST节点的符号表中

   tree.sym = c;

   // Enter class into `compiled table and enclosing scope.

   if (chk.compiled.get(c.flatname) != null) {

   duplicateClass(tree.pos(), c);

   result = types.createErrorType(tree.name, (TypeSymbol)owner, Type.noType);

   tree.sym = (ClassSymbol)result.tsym;

   return;

   chk.compiled.put(c.flatname, c);

   //将类符号c放入当前作用域

   enclScope.enter(c);

   }

 

  

  3.2.访问方法定义

  访问定义在MemberEnter.visitMethodDef()中,与访问类定义类似,首先获取了当前作用域,创建方法符号,然后将符号赋值给当前节点的sym属性,并将符号加入当前作用域

  

public void visitMethodDef(JCMethodDecl tree) {

 

   //获取当前作用域

   Scope enclScope = enter.enterScope(env);

   //创建方法符号

   MethodSymbol m = new MethodSymbol(0, tree.name, null, enclScope.owner);

   m.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, m, tree);

   //将方法符号赋值给当前节点

   tree.sym = m;

   //if this is a default method, add the DEFAULT flag to the enclosing interface

   if ((tree.mods.flags DEFAULT) != 0) {

   m.enclClass().flags_field = DEFAULT;

   //创建当前方法的局部变量作用域

   Env AttrContext localEnv = methodEnv(tree, env);

   annotate.enterStart();

   try {

   //将所有参数加入局部变量作用域

   m.type = signature(m, tree.typarams, tree.params,

   tree.restype, tree.recvparam,

   tree.thrown,

   localEnv);

   //删除当前局部作用域的所有entry

   localEnv.info.scope.leave();

   if (chk.checkUnique(tree.pos(), m, enclScope)) {

   //将方法符号加入当前作用域中

   enclScope.enter(m);

   } finally {

   annotate.enterDone();

   }

 

  

  3.3.访问变量定义

  MemberEnter.visitVarDef()

  

public void visitVarDef(JCVariableDecl tree) {

 

   //获取当前变量所在环境

   Env AttrContext localEnv = env;

   if ((tree.mods.flags STATIC) != 0

   (env.info.scope.owner.flags() INTERFACE) != 0) {

   localEnv = env.dup(tree, env.info.dup());

   localEnv.info.staticLevel++;

   //获取当前作用域

   Scope enclScope = enter.enterScope(env);

   //创建变量符号

   VarSymbol v =

   new VarSymbol(0, tree.name, tree.vartype.type, enclScope.owner);

   v.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, v, tree);

   //将符号赋值给当前节点的sym属性

   tree.sym = v;

   if (chk.checkUnique(tree.pos(), v, enclScope)) {

   chk.checkTransparentVar(tree.pos(), v, enclScope);

   //将变量符号加入作用域

   enclScope.enter(v);

   annotateLater(tree.mods.annotations, localEnv, v, tree.pos());

   typeAnnotate(tree.vartype, env, v, tree.pos());

   v.pos = tree.pos;

   } finally {

   annotate.enterDone();

   }

 

  

  

  以上就是java编译器源码解析(java编译器原理)的详细内容,想要了解更多 java编译器源码解析的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: