项目的完整代码在 C2j-Compiler

前言

在上一篇完成对JVM指令的生成,下面就可以真正进入代码生成部分了。通常现代编译器都是先把生成IR,再经过代码优化等等,最后才编译成目标平台代码。但是时间水平有限,我们没有IR也没有代码优化,就直接利用AST生成Java字节码

入口

进行代码生成的入口在CodeGen,和之前解释器一样:先获取main函数的头节点,从这个节点开始,先进入函数定义,再进入代码块

函数定义节点

在进入函数定义节点的时候,就要生成一个函数定义对应的Java字节码,即一个静态方法(因为我们对整个C语言文件生成为一个类,main方法为public static main,其它的则是对应的静态方法,结构体则是另外的类)

  • 对于函数定义先从节点中拿到对应的函数命和参数
  • emitArgs是用来处理参数的,根据参数生成相应的Java字节码
  • 如果这个函数是main的话已经是交由前面处理了,逻辑差不多(具体在start.Start中)
  1. case SyntaxProductionInit.NewName_LP_RP_TO_FunctDecl:
  2. root.reverseChildren();
  3. AstNode n = root.getChildren().get(0);
  4. String name = (String) n.getAttribute(NodeKey.TEXT);
  5. symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
  6. generator.setCurrentFuncName(name);
  7. if (name != null && !name.equals("main")) {
  8. String declaration = name + emitArgs(symbol);
  9. generator.emitDirective(Directive.METHOD_PUBBLIC_STATIC, declaration);
  10. generator.setNameAndDeclaration(name, declaration);
  11. }
  12. copyChild(root, root.getChildren().get(0));
  13. break;
  14. case SyntaxProductionInit.NewName_LP_VarList_RP_TO_FunctDecl:
  15. n = root.getChildren().get(0);
  16. name = (String) n.getAttribute(NodeKey.TEXT);
  17. symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
  18. generator.setCurrentFuncName(name);
  19. if (name != null && !name.equals("main")) {
  20. String declaration = name + emitArgs(symbol);
  21. generator.emitDirective(Directive.METHOD_PUBBLIC_STATIC, declaration);
  22. generator.setNameAndDeclaration(name, declaration);
  23. }
  24. Symbol args = symbol.getArgList();
  25. if (args == null || argsList == null || argsList.isEmpty()) {
  26. System.err.println("generate function with arg list but arg list is null");
  27. System.exit(1);
  28. }
  29. break;

创建结构体和数组

数组

创建结构体和数组的节点在DefGenerate里,可以看到在这里只处理了数组和普通变量,有关结构体的处理是在对结构体第一次使用的时候。顺便提一下代码生成对于赋初值操作是没有进行处理的。

  • 如果是个数组,酒直接调用ProgramGenerator直接生成创建数组的指令
  • 如果是个普通变量,就直接找到它并且赋值为0(这里变量在队列里的位置是根据符号表来计算的,具体可以看上一篇的getLocalVariableIndex方法)
  1. public class DefGenerate extends BaseGenerate {
  2. @Override
  3. public Object generate(AstNode root) {
  4. int production = (int) root.getAttribute(NodeKey.PRODUCTION);
  5. ProgramGenerator generator = ProgramGenerator.getInstance();
  6. Symbol symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
  7. switch (production) {
  8. case SyntaxProductionInit.Specifiers_DeclList_Semi_TO_Def:
  9. Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
  10. if (declarator != null) {
  11. if (symbol.getSpecifierByType(Specifier.STRUCTURE) == null) {
  12. generator.createArray(symbol);
  13. }
  14. } else {
  15. int i = generator.getLocalVariableIndex(symbol);
  16. generator.emit(Instruction.SIPUSH, "" + 0);
  17. generator.emit(Instruction.ISTORE, "" + i);
  18. }
  19. break;
  20. default:
  21. break;
  22. }
  23. return root;
  24. }
  25. }

结构体

处理结构体定义的代码在UnaryNodeGenerate,也就是只有在使用到结构体定义时才会进行定义

  • 先拿到当前UNARY的符号,如果instanceof ArrayValueSetter就说明是一个结构体数组,就进入getStructSymbolFromStructArray方法创建一个结构体数组,并返回当前下标的结构体对象
  • 设置当前结构体的作用域范围
  • 对结构体作为类进行定义
  • 然后对读取结构体的域
  • 其实可以忽略指针部分,因为代码生成并没有对指针进行模拟
  1. case SyntaxProductionInit.Unary_StructOP_Name_TO_Unary:
  2. child = root.getChildren().get(0);
  3. String fieldName = (String) root.getAttribute(NodeKey.TEXT);
  4. Object object = child.getAttribute(NodeKey.SYMBOL);
  5. boolean isStructArray = false;
  6. if (object instanceof ArrayValueSetter) {
  7. symbol = getStructSymbolFromStructArray(object);
  8. symbol.addValueSetter(object);
  9. isStructArray = true;
  10. } else {
  11. symbol = (Symbol) child.getAttribute(NodeKey.SYMBOL);
  12. }
  13. if (isStructArray) {
  14. ArrayValueSetter vs = (ArrayValueSetter) object;
  15. Symbol structArray = vs.getSymbol();
  16. structArray.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
  17. } else {
  18. symbol.addScope(ProgramGenerator.getInstance().getCurrentFuncName());
  19. }
  20. ProgramGenerator.getInstance().putStructToClassDeclaration(symbol);
  21. if (isSymbolStructPointer(symbol)) {
  22. copyBetweenStructAndMem(symbol, false);
  23. }
  24. Symbol args = symbol.getArgList();
  25. while (args != null) {
  26. if (args.getName().equals(fieldName)) {
  27. args.setStructParent(symbol);
  28. break;
  29. }
  30. args = args.getNextSymbol();
  31. }
  32. if (args == null) {
  33. System.err.println("access a filed not in struct object!");
  34. System.exit(1);
  35. }
  36. if (args.getValue() != null) {
  37. ProgramGenerator.getInstance().readValueFromStructMember(symbol, args);
  38. }
  39. root.setAttribute(NodeKey.SYMBOL, args);
  40. root.setAttribute(NodeKey.VALUE, args.getValue());
  41. if (isSymbolStructPointer(symbol)) {
  42. checkValidPointer(symbol);
  43. structObjSymbol = symbol;
  44. monitorSymbol = args;
  45. GenerateBrocasterImpl.getInstance().registerReceiverForAfterExe(this);
  46. } else {
  47. structObjSymbol = null;
  48. }
  49. break;

一元操作节点

这个节点和在解释器的有很多相同,除了有对结构体的操作,其它的也是有非常重要的作用

  • 像数字、字符串或者是变量和之前的操作都是把信息传递到父节点,交由父节点处理
  1. case SyntaxProductionInit.Number_TO_Unary:
  2. text = (String) root.getAttribute(NodeKey.TEXT);
  3. boolean isFloat = text.indexOf('.') != -1;
  4. if (isFloat) {
  5. value = Float.valueOf(text);
  6. root.setAttribute(NodeKey.VALUE, value);
  7. } else {
  8. value = Integer.valueOf(text);
  9. root.setAttribute(NodeKey.VALUE, value);
  10. }
  11. break;
  12. case SyntaxProductionInit.Name_TO_Unary:
  13. symbol = (Symbol) root.getAttribute(NodeKey.SYMBOL);
  14. if (symbol != null) {
  15. root.setAttribute(NodeKey.VALUE, symbol.getValue());
  16. root.setAttribute(NodeKey.TEXT, symbol.getName());
  17. }
  18. break;
  19. case SyntaxProductionInit.String_TO_Unary:
  20. text = (String) root.getAttribute(NodeKey.TEXT);
  21. root.setAttribute(NodeKey.VALUE, text);
  22. break;
  23. case SyntaxProductionInit.Unary_LB_Expr_RB_TO_Unary:
  24. child = root.getChildren().get(0);
  25. symbol = (Symbol) child.getAttribute(NodeKey.SYMBOL);
  26. child = root.getChildren().get(1);
  27. int index = 0;
  28. if (child.getAttribute(NodeKey.VALUE) != null) {
  29. index = (Integer) child.getAttribute(NodeKey.VALUE);
  30. }
  31. Object idxObj = child.getAttribute(NodeKey.SYMBOL);
  32. try {
  33. Declarator declarator = symbol.getDeclarator(Declarator.ARRAY);
  34. if (declarator != null) {
  35. Object val = declarator.getElement((int) index);
  36. root.setAttribute(NodeKey.VALUE, val);
  37. ArrayValueSetter setter;
  38. if (idxObj == null) {
  39. setter = new ArrayValueSetter(symbol, index);
  40. } else {
  41. setter = new ArrayValueSetter(symbol, idxObj);
  42. }
  43. root.setAttribute(NodeKey.SYMBOL, setter);
  44. root.setAttribute(NodeKey.TEXT, symbol.getName());
  45. }
  46. Declarator pointer = symbol.getDeclarator(Declarator.POINTER);
  47. if (pointer != null) {
  48. setPointerValue(root, symbol, index);
  49. PointerValueSetter pv = new PointerValueSetter(symbol, index);
  50. root.setAttribute(NodeKey.SYMBOL, pv);
  51. root.setAttribute(NodeKey.TEXT, symbol.getName());
  52. }
  53. } catch (Exception e) {
  54. e.printStackTrace();
  55. System.exit(1);
  56. }
  57. break;

赋值操作

  • 如果当前是一个数组,先拿到它的符号和下标
  • 如果不是结构体数组,那么拿到下标直接用readArrayElement生成读取数组元素的指令
  • 如果是一个符号则用getLocalVariableIndex读取这个符号的值
  • 如果是一个常数,则直接生成IPUSH指令
  • 最后进行赋值操作,如果不是对结构体的域进行赋值就直接用getLocalVariableIndex拿到队列位置然后生成ISTORE
  • 如果是对结构体数组的元素的域的赋值,就调用assignValueToStructMemberFromArray生成代码,如果只是结构体就直接调用assignValueToStructMember生成代码
  1. ProgramGenerator generator = ProgramGenerator.getInstance();
  2. if (BaseGenerate.resultOnStack) {
  3. this.value = obj;
  4. BaseGenerate.resultOnStack = false;
  5. } else if (obj instanceof ArrayValueSetter) {
  6. ArrayValueSetter setter = (ArrayValueSetter) obj;
  7. Symbol symbol = setter.getSymbol();
  8. Object index = setter.getIndex();
  9. if (symbol.getSpecifierByType(Specifier.STRUCTURE) == null) {
  10. if (index instanceof Symbol) {
  11. ProgramGenerator.getInstance().readArrayElement(symbol, index);
  12. if (((Symbol) index).getValue() != null) {
  13. int i = (int) ((Symbol) index).getValue();
  14. try {
  15. this.value = symbol.getDeclarator(Declarator.ARRAY).getElement(i);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. } else {
  21. int i = (int) index;
  22. try {
  23. this.value = symbol.getDeclarator(Declarator.ARRAY).getElement(i);
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. ProgramGenerator.getInstance().readArrayElement(symbol, index);
  28. }
  29. }
  30. } else if (obj instanceof Symbol) {
  31. Symbol symbol = (Symbol) obj;
  32. this.value = symbol.value;
  33. int i = generator.getLocalVariableIndex(symbol);
  34. generator.emit(Instruction.ILOAD, "" + i);
  35. } else if (obj instanceof Integer) {
  36. Integer val = (Integer) obj;
  37. generator.emit(Instruction.SIPUSH, "" + val);
  38. this.value = obj;
  39. }
  40. if (!this.isStructMember()) {
  41. int idx = generator.getLocalVariableIndex(this);
  42. if (!generator.isPassingArguments()) {
  43. generator.emit(Instruction.ISTORE, "" + idx);
  44. }
  45. } else {
  46. if (this.getStructSymbol().getValueSetter() != null) {
  47. generator.assignValueToStructMemberFromArray(this.getStructSymbol().getValueSetter(), this, this.value);
  48. } else {
  49. generator.assignValueToStructMember(this.getStructSymbol(), this, this.value);
  50. }
  51. }

最后

完成这部分后,对下面的代码

  1. void quicksort(int A[10], int p, int r) {
  2. int x;
  3. int i;
  4. i = p - 1;
  5. int j;
  6. int t;
  7. int v;
  8. v = r - 1;
  9. if (p < r) {
  10. x = A[r];
  11. for (j = p; j <= v; j++) {
  12. if (A[j] <= x) {
  13. i++;
  14. t = A[i];
  15. A[i] = A[j];
  16. A[j] = t;
  17. }
  18. }
  19. v = i + 1;
  20. t = A[v];
  21. A[v] = A[r];
  22. A[r] = t;
  23. t = v - 1;
  24. quicksort(A, p, t);
  25. t = v + 1;
  26. quicksort(A, t, r);
  27. }
  28. }
  29. void main () {
  30. int a[10];
  31. int i;
  32. int t;
  33. printf("before quick sort:");
  34. for(i = 0; i < 10; i++) {
  35. t = (10 - i);
  36. a[i] = t;
  37. printf("value of a[%d] is %d", i, a[i]);
  38. }
  39. quicksort(a, 0, 9);
  40. printf("after quick sort:");
  41. for (i = 0; i < 10; i++) {
  42. printf("value of a[%d] is %d", i, a[i]);
  43. }
  44. }

则会生成下面的Java字节码

  1. .class public C2Bytecode
  2. .super java/lang/Object
  3. .method public static main([Ljava/lang/String;)V
  4. sipush 10
  5. newarray int
  6. astore 0
  7. sipush 0
  8. istore 1
  9. sipush 0
  10. istore 2
  11. getstatic java/lang/System/out Ljava/io/PrintStream;
  12. ldc "before quick sort:"
  13. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  14. getstatic java/lang/System/out Ljava/io/PrintStream;
  15. ldc "
  16. "
  17. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  18. sipush 0
  19. istore 1
  20. loop0:
  21. iload 1
  22. sipush 10
  23. if_icmpge branch0
  24. sipush 10
  25. iload 1
  26. isub
  27. istore 2
  28. aload 0
  29. iload 1
  30. iload 2
  31. iastore
  32. aload 0
  33. iload 1
  34. iaload
  35. istore 3
  36. iload 1
  37. istore 4
  38. getstatic java/lang/System/out Ljava/io/PrintStream;
  39. ldc "value of a["
  40. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  41. getstatic java/lang/System/out Ljava/io/PrintStream;
  42. iload 4
  43. invokevirtual java/io/PrintStream/print(I)V
  44. getstatic java/lang/System/out Ljava/io/PrintStream;
  45. ldc "] is "
  46. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  47. getstatic java/lang/System/out Ljava/io/PrintStream;
  48. iload 3
  49. invokevirtual java/io/PrintStream/print(I)V
  50. getstatic java/lang/System/out Ljava/io/PrintStream;
  51. ldc "
  52. "
  53. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  54. iload 1
  55. sipush 1
  56. iadd
  57. istore 1
  58. goto loop0
  59. branch0:
  60. aload 0
  61. sipush 0
  62. sipush 9
  63. invokestatic C2Bytecode/quicksort([III)V
  64. getstatic java/lang/System/out Ljava/io/PrintStream;
  65. ldc "after quick sort:"
  66. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  67. getstatic java/lang/System/out Ljava/io/PrintStream;
  68. ldc "
  69. "
  70. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  71. sipush 0
  72. istore 1
  73. loop2:
  74. iload 1
  75. sipush 10
  76. if_icmpge branch4
  77. aload 0
  78. iload 1
  79. iaload
  80. istore 3
  81. iload 1
  82. istore 4
  83. getstatic java/lang/System/out Ljava/io/PrintStream;
  84. ldc "value of a["
  85. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  86. getstatic java/lang/System/out Ljava/io/PrintStream;
  87. iload 4
  88. invokevirtual java/io/PrintStream/print(I)V
  89. getstatic java/lang/System/out Ljava/io/PrintStream;
  90. ldc "] is "
  91. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  92. getstatic java/lang/System/out Ljava/io/PrintStream;
  93. iload 3
  94. invokevirtual java/io/PrintStream/print(I)V
  95. getstatic java/lang/System/out Ljava/io/PrintStream;
  96. ldc "
  97. "
  98. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  99. iload 1
  100. sipush 1
  101. iadd
  102. istore 1
  103. goto loop2
  104. branch4:
  105. return
  106. .end method
  107. .method public static quicksort([III)V
  108. sipush 2
  109. newarray int
  110. astore 6
  111. sipush 0
  112. istore 5
  113. sipush 1
  114. istore 5
  115. aload 6
  116. iload 5
  117. sipush 1
  118. iastore
  119. aload 6
  120. sipush 1
  121. iaload
  122. istore 10
  123. getstatic java/lang/System/out Ljava/io/PrintStream;
  124. ldc "before quick sort: "
  125. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  126. getstatic java/lang/System/out Ljava/io/PrintStream;
  127. iload 10
  128. invokevirtual java/io/PrintStream/print(I)V
  129. getstatic java/lang/System/out Ljava/io/PrintStream;
  130. ldc "
  131. "
  132. invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V
  133. sipush 0
  134. istore 9
  135. sipush 0
  136. istore 3
  137. iload 1
  138. sipush 1
  139. isub
  140. istore 3
  141. sipush 0
  142. istore 4
  143. sipush 0
  144. istore 7
  145. sipush 0
  146. istore 8
  147. iload 2
  148. sipush 1
  149. isub
  150. istore 8
  151. iload 1
  152. iload 2
  153. if_icmpge branch1
  154. aload 0
  155. iload 2
  156. iaload
  157. istore 9
  158. iload 1
  159. istore 4
  160. loop1:
  161. iload 4
  162. iload 8
  163. if_icmpgt ibranch1
  164. aload 0
  165. iload 4
  166. iaload
  167. iload 9
  168. if_icmpgt ibranch2
  169. iload 3
  170. sipush 1
  171. iadd
  172. istore 3
  173. aload 0
  174. iload 3
  175. iaload
  176. istore 7
  177. aload 0
  178. iload 3
  179. aload 0
  180. iload 4
  181. iaload
  182. iastore
  183. aload 0
  184. iload 4
  185. iload 7
  186. iastore
  187. ibranch2:
  188. iload 4
  189. sipush 1
  190. iadd
  191. istore 4
  192. goto loop1
  193. ibranch1:
  194. iload 3
  195. sipush 1
  196. iadd
  197. istore 8
  198. aload 0
  199. iload 8
  200. iaload
  201. istore 7
  202. aload 0
  203. iload 8
  204. aload 0
  205. iload 2
  206. iaload
  207. iastore
  208. aload 0
  209. iload 2
  210. iload 7
  211. iastore
  212. iload 8
  213. sipush 1
  214. isub
  215. istore 7
  216. aload 0
  217. iload 1
  218. iload 7
  219. invokestatic C2Bytecode/quicksort([III)V
  220. iload 8
  221. sipush 1
  222. iadd
  223. istore 7
  224. aload 0
  225. iload 7
  226. iload 2
  227. invokestatic C2Bytecode/quicksort([III)V
  228. branch1:
  229. return
  230. .end method
  231. .end class

小结

这篇的代码生成和之前解释器的思路很相似,都是根据AST和对应的产生式来执行或者生成代码。

其实主要的思路是很清晰的,只是其中有太多细节容易让人太过纠结。这个系列算作是我自己的学习笔记,到这也有十三篇了,下一篇可能写写总结就正式结束了。

欢迎Star!

从零写一个编译器(十三):代码生成之遍历AST的更多相关文章

  1. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  2. 从零写一个编译器(十一):代码生成之Java字节码基础

    项目的完整代码在 C2j-Compiler 前言 第十一篇,终于要进入代码生成部分了,但是但是在此之前,因为我们要做的是C语言到字节码的编译,所以自然要了解一些字节码,但是由于C语言比较简单,所以只需 ...

  3. 手把手教你从零写一个简单的 VUE

    本系列是一个教程,下面贴下目录~1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 今天给大家带来的是实现一个简单的类似 VUE 一样的前端框架,VUE 框架现在应 ...

  4. 手把手教你从零写一个简单的 VUE--模板篇

    教程目录1.手把手教你从零写一个简单的 VUE2.手把手教你从零写一个简单的 VUE--模板篇 Hello,我又回来了,上一次的文章教会了大家如何书写一个简单 VUE,里面实现了VUE 的数据驱动视图 ...

  5. 学了编译原理能否用 Java 写一个编译器或解释器?

    16 个回答 默认排序​ RednaxelaFX JavaScript.编译原理.编程 等 7 个话题的优秀回答者 282 人赞同了该回答 能.我一开始学编译原理的时候就是用Java写了好多小编译器和 ...

  6. 从零写一个Asp.net core手脚架(模型验证)

    一个asp.net core项目,一定包含了各种的实体,在RESTful api里面,有很多的参数传递,不建立实体则大量的参数需要自定验证正确性,并且Action上面会写的密密麻麻的参数 在asp.n ...

  7. 从零写一个Asp.net core手脚架 (异常处理)

    既然是手脚架,那么肯定得明白,手脚架是有限资源的一个整合,我们尽可能完善它,并保留可扩展性才是最终目的,尽可能减少硬编码,让业务不满足的情况下,可以自行修改 我们把解决方案取名Asp.netCoreT ...

  8. 自己动手写一个编译器Tiny语言解析器实现

    然后,上一篇文章简介Tiny词法分析,实现语言.本文将介绍Tiny的语法分析器的实现. 1 Tiny语言的语法 下图是Tiny在BNF中的文法. 文法的定义能够看出.INNY语言有以下特点: 1 程序 ...

  9. 怎样写一个webpack loader

    div{display:table-cell;vertical-align:middle}#crayon-theme-info .content *{float:left}#crayon-theme- ...

随机推荐

  1. 算法学习笔记,几个简单的Demo

    算法初学的一些心得 前言:现在工作也快一年多了,有时间下班回家会学学算法,陆陆续续也接触了一些 貌似我知道的就冒泡排序其他的都不是很了解 最近买了一本书,边学边记录吧! 一些常用的方法 暴力破解 下面 ...

  2. 【原】深度学习的一些经验总结和建议 | To do v.s Not To Do

    前言:本文同步发布于公众号:Charlotte数据挖掘,欢迎关注,获得最新干货- 昨天看到几篇不同的文章写关于机器学习的to do & not to do,有些观点赞同,有些不赞同,是现在算法 ...

  3. LiteDB源码解析系列(1)LiteDB介绍

    最近利用端午假期,我把LiteDB的源码仔细的阅读了一遍,酣畅淋漓,确实收获了不少.后面将编写一系列关于LteDB的文章分享给大家,希望这么好的源码不要被埋没. 1.LiteDB是什么 这是一个小型的 ...

  4. springboot启动不设置端口

    非web工程 在服务架构中,有些springboot工程只是简单的作为服务,并不提供web服务 这个时候不需要依赖 <dependency> <groupId>org.spri ...

  5. 常用socket函数详解

    常用socket函数详解 关于socket函数,每个的意义和基本功能都知道,但每次使用都会去百度,参数到底是什么,返回值代表什么意义,就是说用的少,也记得不够精确.每次都查半天,经常烦恼于此.索性都弄 ...

  6. wp伪静态网页打开慢之提速方案1s内打开 wp的静态化插件测试

    自上篇文章,我做了伪静态话.可是伪静态访问还是php动态页面,还需要服务端分析如何处理,访问页面时会发现有一个漫长的等待响应的时间.这是打开速度在4s左右.而静态页面则是直接打开,不需要服务器操作,不 ...

  7. 关于keil警告/错误问题的解释和修正

    - 版权声明:本文为博主 **乔勇刚-** 一字一句敲出来的原创作品,未经博主允许不得转载,多谢支持.- 本系列博客仅做经验交流分享,不能用作任何商业用途.本文中如有不足之处,请您留言,本人将及时更改 ...

  8. RabbitMQ(三):RabbitMQ与Spring Boot简单整合

    RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用.Spring Boot的兴起,极大地简化了Spring的开发,本文将使用Spring Boot与RabbitM ...

  9. JNDI总结(一)

    一.数据源的由来 在Java开发中,使用JDBC操作数据库的四个步骤如下:   ①加载数据库驱动程序(Class.forName("数据库驱动类");)   ②连接数据库(Conn ...

  10. 比特币and区块链

    比特币简介 比特币(Bitcoin:比特金)最早是一种网络虚拟货币,可以购买现实生活当中的物品.它的特点是分散化.匿名.只能在数字世界使用,不属于任何国家和金融机构,并且不受地域的限制,可以在世界上的 ...