导航:首页 > 源码编译 > 编译阶段修改注解

编译阶段修改注解

发布时间:2023-06-13 17:54:52

编译过程分为哪几个阶段各阶段的遵循的原则、识别机构、使用的文法编译原理

编译原理中的遍概念
编译阶段也常常划分为两大步骤,分析步骤和综合步骤 分析步骤和综合步骤 分析步骤是指对源程序的分析 -线性分析(词法分析或扫描) -层次分析(语法分析) -语义分析 综合步骤是指后端的工作,为目标程序的生成而进行的综合

你分析过吗?若按照这种组合方式实现编译程序,可以设想,某一编译程序的前端加上相应不同的后 端则可以为不同的机器构成同一个源语言的编译程序。也可以设想,不同语言编译的前端生成同一种中间 语言,再使用一个共同的后端,则可为同一机器生成几个语言的编译程序。

一个编译过程可由一遍、两遍或多遍完成。所谓"遍",也称作"趟",是对源程序或其等价的中间语言程 序从头到尾扫视并完成规定任务的过程。每一遍扫视可完成上述一个阶段或多个阶段的工作。例如一遍可 以只完成词法分析工作;一遍完成词法分析和语法分析工作;甚至一遍完成整个编译工作。对于多遍的编 译程序,第一遍的输入是用户书写的源程序,最后一遍的输出是目标语言程序,其余是上一遍的输出为下 一遍的输入。

在实际的编译系统的设计中,编译的几个阶段的工作究竟应该怎样组合,即编译程序究竟分成几遍, 参考的因素主要是源语言和机器(目标机)的特征。比如源语言的结构直接影响编译的遍的划分;像 PL/1 或 ALGOL 68 那样的语言,允许名字的说明出现在名字的使用之后,那么在看到名字之前是不便为包含该名 字的表达式生成代码的,这种语言的编译程序至少分成两遍才容易生成代码。另外机器的情况,即编译程 序工作的环境也影响编译程序的遍数的划分。遍数多一点,整个编译程序的逻辑结构可能清晰些,但遍数 多即意味着增加读写中间文件的次数,势必消耗较多时间,一般会比一遍的编译要慢。

Ⅱ 如何写一个编译时注解框架

1、注解

我们日常使用的很多开源库都有注解的实现,主要有运行时注解和编译时注解两种。
运行时注解:主要作用就是得到注解的信息
Retrofit:
调用

@GET("/users/{username}")
User getUser(@Path("username") String username);

定义
@Documented
@Target(METHOD)
@Retention(RUNTIME)
@RestMethod("GET")
public @interface GET {
String value();
}

编译时注解:主要作用动态生成代码

Butter Knife

调用
@InjectView(R.id.user)
EditText username;

定义
@Retention(CLASS)
@Target(FIELD)
public @interface InjectView {
int value();
}

2、编译时注解

要实现编译时注解需要3步:

1、定义注解(关于注解的定义可以参考下面引用的博客)
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;

@Target({ ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.CLASS)
public @interface Seriable
{

}

2、编写注解解析器
@SupportedAnnotationTypes("annotation.Seriable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class ViewInjectProcessor extends AbstractProcessor {

@Override
public boolean process(Set annotations,
RoundEnvironment roundEnv) {

for (Element ele : roundEnv.getElementsAnnotatedWith(InjectView.class)) {
if(ele.getKind() == ElementKind.FIELD){
//todo
}

return true;
}

@SupportedAnnotationTypes,定义要支持注解的完整路径,也可以通过getSupportedAnnotationTypes方法来定义
@Override
public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(InjectView.class.getCanonicalName());

return types;
}

@SupportedSourceVersion(SourceVersion.RELEASE_6)表示支持的jdk的版本
3、创建制定文件resources/META-INF/services/javax.annotation.processing.Processor,并填写注解解析器的类路径,这样在编译的时候就能自动找到解析器

看上去实现编译时注解还是很容易的,但是真要完整的实现一个类似Butter Knife的框架,这还只是开始。
Butter Knife是专注View的注入,在使用注解的类编译后,查看编译后的class文件,会发现多出了文件,如:
SimpleActivity$$ViewInjector.java
SimpleActivity$$ViewInjector.java,就是通过编译时注解动态创建出来的,查看SimpleActivity$$ViewInjector.java的内容
// Generated code from Butter Knife. Do not modify!
package com.example.butterknife;

import android.view.View;

import butterknife.ButterKnife.Finder;

public class SimpleActivity$$ViewInjector {

public static void inject(Finder finder, final com.example.butterknife.SimpleActivity target, Object source) {
View view;
view = finder.findRequiredView(source, 2131230759, "field 'title'");
target.title = (android.widget.TextView) view;
view = finder.findRequiredView(source, 2131230783, "field 'subtitle'");
target.subtitle = (android.widget.TextView) view;
view = finder.findRequiredView(source, 2131230784, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");
target.hello = (android.widget.Button) view;
view.setOnClickListener(
new butterknife.internal.DebouncingOnClickListener() {
@Override
public void doClick(
android.view.View p0
) {
target.sayHello();
}
});
view.setOnLongClickListener(
new android.view.View.OnLongClickListener() {
@Override
public boolean onLongClick(
android.view.View p0
) {
return target.sayGetOffMe();
}
});
view = finder.findRequiredView(source, 2131230785, "field 'listOfThings' and method 'onItemClick'");
target.listOfThings = (android.widget.ListView) view;
((android.widget.AdapterView) view).setOnItemClickListener(
new android.widget.AdapterView.OnItemClickListener() {
@Override
public void onItemClick(
android.widget.AdapterView p0,
android.view.View p1,
int p2,
long p3
) {
target.onItemClick(p2);
}
});
view = finder.findRequiredView(source, 2131230786, "field 'footer'");
target.footer = (android.widget.TextView) view;
}

public static void reset(com.example.butterknife.SimpleActivity target) {
target.title = null;
target.subtitle = null;
target.hello = null;
target.listOfThings = null;
target.footer = null;
}
}

inject方法进行初始化,reset进行释放。inject都是调用Finder的方法与android系统的findViewById等方法很像,再来看Finder类,只截取部分。

public enum Finder {
public T findRequiredView(Object source, int id, String who) {
T view = findOptionalView(source, id, who);
if (view == null) {
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
return view;
}

public T findOptionalView(Object source, int id, String who) {
View view = findView(source, id);
return castView(view, id, who);
}

@Override protected View findView(Object source, int id) {
return ((View) source).findViewById(id);
}

@SuppressWarnings("unchecked") // That's the point.
public T castView(View view, int id, String who) {
try {
return (T) view;
} catch (ClassCastException e) {
if (who == null) {
throw new AssertionError();
}
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}

findRequiredView方法实际上就是我们常用findViewById的实现,其动态帮我们添加了这些实现。view注入的原理我们就清楚了。
虽然编译时创建了这个类,运行的时候如何使用这个类呢,这里我用自己实现的一个类来描述
public class XlViewInjector {

static final Map, AbstractInjector> INJECTORS = new LinkedHashMap, AbstractInjector>();

public static void inject(Activity activity){
AbstractInjector injector = findInjector(activity);
injector.inject(Finder.ACTIVITY, activity, activity);
}

public static void inject(Object target, View view){
AbstractInjector injector = findInjector(target);
injector.inject(Finder.VIEW, target, view);
}

private static AbstractInjector findInjector(Object target){
Class clazz = target.getClass();
AbstractInjector injector = INJECTORS.get(clazz);
if(injector == null){
try{
Class injectorClazz = Class.forName(clazz.getName()+"$$"+ProxyInfo.PROXY);
injector = (AbstractInjector) injectorClazz.newInstance();
INJECTORS.put(clazz, injector);
}catch(Exception e){
e.printStackTrace();
}
}

return injector;
}
}
XlViewInjector与ButterKnife,比如调用时我们都会执行XlViewInjector.inject方法,通过传入目标类的名称获得封装后的类实例就是SimpleActivity$$ViewInjector.java,再调用它的inject,来初始化各个view。
总结一下整个实现的流程:
1、通过编译时注解动态创建了一个包装类,在这个类中已解析了注解,实现了获取view、设置监听等代码。
2、执行时调用XlViewInjector.inject(object)方法,实例化object类对应的包装类,并执行他的初始化方法inject;
因此我们也能明白为什么XlViewInjector.inject(object)方法一定要在setContentView之后执行。

3、实现注解框架时的坑

解析有用到android api,因此需要创建Android工程,但是android library并没有javax的一些功能,
在eclipse环境下,右键build-path add library把jdk加进来
在android studio下,需要先创建java Library功能,实现与view无关的解析,再创建一个android library功能引用这个工程并实现余下的解析。

Ⅲ 程序进行预编译处理时将每个注释替换为一个空格是什么意思,有什么用

可以这么理解:注释是为人增加代码可读性用的,编译过程不需要,所以在预处理阶段编译器会去掉方便后续的动作

Ⅳ 问下计算机大神,C语言编译执行时会自动去掉源代码的注释符,那么这个去掉是有规则的还是无规则的亦或

一般情况下是不会反编译出来的,但也有例外。
c语言是在预处理阶段把所有的注释全部干掉的。编译器不接受任何注释,会直接报错。
如果你的IDE能提供一个类似于插件之类的工具,在c程序提交编译之前自动把你所有的注释变进程序正文中做成那种由双引号标识的字符串(例如添加一条语句char * _comm_1="Built on 2013-11-11";),而且之后没有使用类似于混淆器这样的防止反编译的技术,那么对软件反汇编后是可以看到它们的。
但是正常情况下别人是不可能直接从软件反编译的结果上去找你的程序注释的。

Ⅳ c语言程序编译时,注释部分会参加编译吗,会出现在目标程序中吗

不会的,因为注释被忽略了。

C语言的原型ALGOL60语言(也称为A语言)。

1963年,剑桥大学将ALGOL60语言发展成为CPL(CombinedProgrammingLanguage)语言。

1967年,剑桥大学的MatinRichards对CPL语言进行了简化,于是产生了BCPL语言。

1970年,美国贝尔实验室的KenThompson将BCPL进行了修改,并为它起了一个有趣的名字“B语言”。意思是将CPL语言煮干,提炼出它的精华。并且他用B语言写了第一个UNIX操作系统。

Ⅵ Java编译时注解和运行时注解有什么区别

重写,重载,泛型,分别是在运行时还是编译时执行的

1. 方法重载是在编译时执行的,因为,在编译的时候,如果调用了一个重载的方法,那么编译时必须确定他调用的方法是哪个。如:

当调用evaluate("hello")时候,我们在编译时就可以确定他调用的method #1.

2.
方法的重写是在运行时进行的。这个也常被称为运行时多态的体现。编译器是没有办法知道它调用的到底是那个方法,相反的,只有在jvm执行过程中,才知晓到底是父子类中的哪个方法被调用了当有如下一个接口的时候,我们是无法确定到底是调用父类还是子类的方法

3.
泛型(类型检测),这个发生在编译时。编译器会在编译时对泛型类型进行检测,并吧他重写成实际的对象类型(非泛型代码),这样就可以被JVM执行了。这个过程被称为"类型擦除"。

类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。

类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。类型擦除的主要过程如下:

1). 将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。

2). 移除所有的类型参数。

在编译后变成:

4. 注解。注解即有可能是运行时也有可能是编译时。

如java中的@Override注解就是典型的编译时注解,他会在编译时会检查一些简单的如拼写的错误(与父类方法不相同)等

同样的@Test注解是junit框架的注解,他是一个运行时注解,他可以在运行时动态的配置相关信息如timeout等。

5. 异常。异常即有可能是运行时异常,也有可能是编译时异常。

RuntimeException是一个用于指示编译器不需要检查的异常。RuntimeException
是在jvm运行过程中抛出异常的父类。对于运行时异常是不需要再方法中显示的捕获或者处理的。

已检查的异常是被编译器在编译时候已经检查过的异常,这些异常需要在try/catch块中处理的异常。

6. AOP. Aspects能够在编译时,预编译时以及运行时使用。

1).
编译时:当你拥有源码的时候,AOP编译器(AspectJ编译器)能够编译源码并生成编织后的class。这些编织进入的额外功能是在编译时放进去的。

2). 预编译时:织入过程有时候也叫二进制织入,它是用来织入到哪些已经存在的class文件或者jar中的。

3). 运行时:当被织入的对象已经被加载如jvm中后,可以动态的织入到这些类中一些信息。

7. 继承:继承是编译时执行的,它是静态的。这个过程编译后就已经确定

8. 代理(delegate):也称动态代理,是在运行时执行。

Ⅶ C语言文件的编译与执行的四个阶段并分别描述

开发C程序有四个步骤:编辑、编译、连接和运行。

任何一个体系结构处理器上都可以使用C语言程序,只要该体系结构处理器有相应的C语言编译器和库,那么C源代码就可以编译并连接到目标二进制文件上运行。

1、预处理:导入源程序并保存(C文件)。

2、编译:将源程序转换为目标文件(Obj文件)。

3、链接:将目标文件生成为可执行文件(EXE文件)。

4、运行:执行,获取运行结果的EXE文件。

(7)编译阶段修改注解扩展阅读:

将C语言代码分为程序的几个阶段:

1、首先,源代码文件测试。以及相关的头文件,比如stdio。H、由预处理器CPP预处理为.I文件。预编译的。文件不包含任何宏定义,因为所有宏都已展开,并且包含的文件已插入。我归档。

2、编译过程是对预处理文件进行词法分析、语法分析、语义分析和优化,生成相应的汇编代码文件。这个过程往往是整个程序的核心部分,也是最复杂的部分之一。

3、汇编程序不直接输出可执行文件,而是输出目标文件。汇编程序可以调用LD来生成可以运行的可执行程序。也就是说,您需要链接大量的文件才能获得“a.out”,即最终的可执行文件。

4、在链接过程中,需要重新调整其他目标文件中定义的函数调用指令,而其他目标文件中定义的变量也存在同样的问题。

阅读全文

与编译阶段修改注解相关的资料

热点内容
dvd光盘存储汉子算法 浏览:758
苹果邮件无法连接服务器地址 浏览:963
phpffmpeg转码 浏览:672
长沙好玩的解压项目 浏览:145
专属学情分析报告是什么app 浏览:564
php工程部署 浏览:833
android全屏透明 浏览:737
阿里云服务器已开通怎么办 浏览:803
光遇为什么登录时服务器已满 浏览:302
PDF分析 浏览:486
h3c光纤全工半全工设置命令 浏览:143
公司法pdf下载 浏览:383
linuxmarkdown 浏览:350
华为手机怎么多选文件夹 浏览:683
如何取消命令方块指令 浏览:350
风翼app为什么进不去了 浏览:779
im4java压缩图片 浏览:362
数据查询网站源码 浏览:151
伊克塞尔文档怎么进行加密 浏览:893
app转账是什么 浏览:163