Author:刘峻松
1问题的提出
1.1 Java简介
Java是一种使用非常广泛的编程语言,它的执行过程是先将源代码编译成标准的中间代码,并交给JAVA虚拟机(Java Virtual Machine,简称JVM)来解释执行。这样就要求Java代码必须先编译,再解释执行,同时Java代码的执行必须依赖于JVM的支持。
1.2 特殊应用场景
在很多情况下,我们希望能够让Java代码直接以解释的方式来执行,跳过编译成中间代码这一步,这样才能更好的满足实际应用的需求,这样可能的场景包括:
Ø 让Java代码脱离JVM的环境来运行,例如在浏览器或者手机环境上直接使用Java语言,或者将现有Java语言编写的功能迁移到非JVM环境,这时就需要实现一个Java解释器;
Ø 出于安全性考虑,在JVM上设计开发一个独立的安全沙盒(Sandbox),在这个沙盒内运行Java代码,由于沙盒是需要限制特定功能的,例如可以限制不可使用本地文件系统,避免非法访问造成泄密,这种情况下,就需要沙盒本身提供可定制化的功能,同时沙盒也需要具有对Java代码进行解释执行的功能;
Ø 在JVM上实现Java代码的动态加载执行,代码本身可以存放在外部文件存储或者数据库中,在执行时再读入进行解释执行,这样就避免了后台服务的频繁更新发布,后台服务器本身演化成了一个Java代码的解释执行的容器,相当于在JVM之上,又挂接了一个Java的解释执行环境
Ø Java代码的单步执行及调试支持,目前的Java代码单步执行和断点调试功能,是依赖于IDE环境来实现的,一旦打包发布以后,是没有办法在服务器上直接进行单步调试的,或者需要加入第三方产品来实现,相对比较困难。在很多应用场景,需要在服务器上能够支持代码的单步运行和断点调试,这时也需要在JVM上提供Java解释执行的功能;
Ø 中文编程的支持,中文编程需要考虑增加中文的关键词和特殊的语法定义,这种情况下如果有一个完整的Java解释器,哪怕只是一个实现部分功能的解释器原型,那么中文编程的问题就会更加容易解决;
Ø 分布式执行调度,如果可以把Java代码直接由调度者发送给执行者进行执行,整个分布式应用的调度工作和执行反馈工作就会变得更加清晰,更加简单;
1.3 SmallJava解释器定义
基于以上的考虑,我们设计开发出了SmallJava解释器,它不是一个全功能的Java语言解释器(那样工作量太过庞大了,完成日期将遥遥无期),而是有选择的实现了最常用特性的Java语言的一个子集,通过构建一个Java运行环境,使得Java语言可以以脚本的形式和Java类文件的形式由SmallJava解释执行。SmallJava解释器对于上面提到的各种应用场景都能够很好的给予支持,具体支持方式和设计方案将在后面章节进行详细讨论,详见第4章。
SmallJava解释器的一大特点是从零开始构建,它并没有依赖和调用其他的第三方工具和组件,而是仅依赖于JDK本身提供的API来实现,这样也使得它在未来可以相对容易的迁移到其他开发语言例如JavaScript、Android、IOS、Python、C#来二次进行实现。
2总体设计
2.1理论基础
2.1.1编译原理简介
和其他语言解释器一样,SmallJava也遵循编译原理的基本规则来进行设计实现。编译原理将从源代码到最终二进制代码的过程标准化分解为词法分析、语法分析、语义分析、中间代码生成、代码优化、机器代码生成、机器代码优化这样标准的几个步骤,利用编译原理就可以实现从源代码到最终机器代码的完整转换过程。
编译器的本质是将人类可读的代码格式转换成计算机程序可读的标准化格式。
2.1.2栈式解释器与非栈式解释器
栈是一种相当常见的基础数据结构,它使用先进后出的方式来执行,在很多的编译语言设计中,都大量使用了栈作为基本数据结构,但栈的大量使用,也使得程序难以编写,难以理解,为简化程序结构起见,SmallJava解释器在设计时采用非栈式设计,没有选择使用栈作为基础的数据结构,这一点在阅读源代码时需要注意。
2.1.3 SmallJava抽象升级
SmallJavaV1.0核心抽象
由于SmallJavaV1.0的设计目的是提供一个Java脚本的解释执行环境,出于实用性考虑,同时为了简化实现代码,使代码更加容易阅读和理解,便于以后升级扩展,因此在具体设计时,并没有严格按照编译原理来进行实现,而是进行了大量的简化工作。
从宏观上来说,SmallJava抽象为控制流部分和数据流部分。公式描述如下:
SmallJava =【Control flow】+【Data flow】
如果使用更下一层的抽象模型,抽象为如下公式:
SmallJava = 【Block】+【Expression】+【VARTABLE】+【ClassTable】
SmallJava整体上就是由这四个逻辑概念部分组成的,分别是块、表达式、变量表、类定义表。因此从原理上来说,只要能够完整支持这四部分逻辑概念的编程语言,都可以作为宿主语言来实现SmallJava解释器,从而间接实现对Java语言的解释执行支持。
SmallJavaV2.0核心抽象
在SmallJavaV1.0的基础上,SmallJava2.0的核心概念进行了重新设计和概念的扩展,SmallJavaV1.0的设计目的是做一个单纯的脚本解释器,以解释方式来执行一段Java代码,最终的调用入口输入的是一个Java代码块,这种调用方式不支持Java本身的类,实例,方法调用等概念,而仅仅限制在一个脚本的层次。
SmallJavaV2.0在SmallJavaV1.0的基础上,从Java语言的基本原理出发,扩展支持到了Java类文件、Java类、Java方法、代码块、表达式五个新的层次,从而在设计概念上真正实现了和Java语言的全面兼容。
2.2 SmallJava核心设计
2.2.1 SmallJava设计方法论(核心内容)
对于任何一个规模较大的程序系统来说,完成单独一个功能点的设计并不算很困难,但如何将各个单独的功能点分解组合起来构成一个有机的整体,在出现问题的时候能够快速定位,准确解决,这是一个比较困难的问题,毕竟软件系统的复杂性是其本质性特点。
SmallJava经过V1.0,V2.0两个版本的设计,目前整体上采用两个相互正交的维度来进行功能的设计和分解,第一个维度是采用空间和时间分离的维度,将所有功能分解到空间维度或者时间维度;第二个维度是采用对象分层的维度,将要处理的程序源代码,抽象出文件、类、方法、块、表达式五个层次,这样两个正交的分解维度(时空维度+层次维度)就将整个SmallJava分解成10大部分。代码分析时层层递进分解到最小单元;代码执行时则依赖于层次性的变量表来实现变量的作用域。这两个相互正交的维度,是整个SmallJava最为核心的设计理念。
2.2.2 控制流和数据流
一个解释器所接收的输入是一段字符串形式的程序代码,在执行过程中,实际上存在两类不同类型的信息,一类是纯粹的程序执行流程的控制,即一步执行完毕以后,下一步去那里寻找的的问题,这部分我们称为“控制流”,控制流部分在解释器内部执行,不需要调用外部资源,不对内存等资源进行操作;另一部分是最终分解完成的表达式,这部分的执行,最后会转换成对变量的读写,外部类实例的调用,JVM上内存的分配,新对象的生成,这部分我们成为“数据流”。
控制流使用Block来进行抽象,数据流使用Expression来进行抽象实现。
2.2.3 空间和时间(Space and Time)
SmallJava解释器是从空间和时间的角度来做设计分解的。这一特殊角度使得SmallJava解释器与众不同。
以前有一本奇书,叫《编程之道》(The Tao of Programming),其中有这样一段。世间万物皆有阴阳,空间和时间就是程序的阴和阳。
原文引用如下:
In the beginning was the Tao. | |
The Tao gave birth to Space and Time. | |
Therefore Space and Time are the Yin and Yang of programming. |
直译如下:
太初有道;
道产生了空间和时间;
这样空间和时间就构成了程序的阴和阳。
我的理解:
空间可以理解为程序所占用的资源,包括自身的代码空间,自身所申请开辟的内存空间,再广义的可以扩展为所有的外部资源,包括文件、网络、数据库等。但这种占用是静态的,本身不会发生自己改变。
而时间则是类似与步进电机一样,一次前进一格。在计算机体系内称为一个CPU时种,由振荡器定时发出一个CPU中断信息,驱动CPU执行下一条指令。
在SmallJava语言中,空间被抽象为变量表和类定义表,而时间则被抽象为AST节点,包括Block AST和Expression AST。AST本身就是按步骤执行的抽象。
空间通过时间的运动而发生改变;变量表通过AST节点的执行来发生改变。
从这个意义上讲,只有表达式的AST执行器才是真正纯粹的时间要素,其他的都是为它来提供支撑和服务的,广义来说,把整个AST的部分都划分到时间要素里面也是可以的。
从SmallJava解释器的角度来看,它本身所处的外部运行环境,都是空间的概念,是相对静止保持不变的,而它自己则是时间的概念,可以运行,并通过运行来改变外部的运行环境,也就是改变空间。SmallJava本身和外部的交互点,主要就体现在变量表上。变量表中的变量,其实际存储位置是在外部的空间上,但对于这些空间的读写,对象调用,命令是由SmallJava解释器发起的,从这个意义上讲,SmallJava解释器是相对于外部环境的空间元素的时间性存在,通过SmallJava本身的运行,完成了对外部运行环境空间的实际改变。
换句话说,只要能够解决Space和Time的问题,那么编程语言自己也就呼之欲出了。
2.2.4 SmallJava 核心分层设计
SmallJava在AST分解时,采用的是分层设计的方式,以原始的Java 源代码文件作为分析入口,逐层进行逻辑分解,最终分解得到可以执行的表达式叶子节点,到了这一步才能进行真正的数据计算工作。
2.2.5 SmallJava代码架构分解(核心设计)
SmallJava代码从两个维度进行代码分解,从横向角度,是从原始输入、AST语法分解、标准VO对象、Eval执行器、变量表这样来区分;从纵向角度,是从不同抽象层次进行分解。代码架构分解图如下:
2.2.6 Block AST与Expression AST
AST,即抽象语法树(Abstract Syntax Tree),是源代码进行转换以后的标准化内部结构,传统上在经过词法分析和句法分析以后,源代码就会转换成内部的标准数据结构,即AST。
在SmallJava设计中,设计了两种不同的AST,一种代表程序块(Block),一种代表表达式(Expression)。源代码先以Block作为基础单位进行AST分析,在执行时,再将其中最基础的表达式抽取出来,进行表达式的AST分析,AST执行。
2.2.7 SmallJavaV1.0设计总图
这张图上紫色的部分是逻辑抽象,绿色的部分是实际代码实现的部分。
SmallJava V1.0版本设计时只有Block和Expression两个层次,V1.0设计的定位是对Java脚本进行解析,还没有上升到Java文件,和Java类的层次。
2.2.8 SmallJavaV2.0 AST分层结构
SmallJavaV2.0在上面这张图的基础上进行了逻辑扩展,增加了JavaFile、JavaClass、Method这样三个层次的AST,每一个层次AST的输出结果作为下一个层次AST分解的基本输入来使用。
这是纵向的AST调用层次图,每一个上层的AST分析的输出结果,作为下一层次的AST分析的输入项。通过这种层次型分解模式,将最初输入的Java源文件,最终分解为Java表达式的最基本元素,包括常量、变量、算术运算符、逻辑运算符等。
2.2.9 SmallJavaV2.0变量表分层设计
Java语句在执行时,需要解决变量的可见性问题,在当前执行环境变量不能解析时,需要上溯到更上一级的执行环境来尝试解析,依次上溯到最初的执行环境进行解析,如果仍然不能正常解析,则变量不能解析,出现执行时错误。为解决这一问题,在SmallJavaV2.0中,参照核心的分层结构,变量表也设计成对应的分层结构来实现。每一个下层的变量表对象,都设置其父节点的上层变量表对象,逐层嵌套来满足变量作用域的解析要求。
变量表和AST一样,都采用分层方式进行设计,两者的区别在于,AST的分层设计,上次的分析结果是下层AST的输入,是一种串联关系;而变量表则是采用链表技术构建成了一个整体,通过设置父节点的方法,变量表实现了逐层上溯的搜索算法。
2.3 JavaFile设计
JavaFile是整个Java文件解析的总入口,负责读取Java源代码文件,进行第一层次的文本解析。
2.3.1 JavaFile元素定义
2.3.2 JavaFile分析器
JavaFile分析器,读入原始的文件定义字符串,按照Element元素规则进行分解。
2.3.3 JavaFile执行器
JavaFile执行器执行时,根据Import元素写入ClassTable.
2.3 JavaClass设计
JavaClass定义为Java的Class定义语句,其关键词是【class】。
2.3.1 Class元素定义
2.3.2 Class分析器
Class分析器以JavaFile的分析结果作为输入,按照Element元素规则进行分解。
2.3.3 Class执行器
Class执行器包括两个方法,一个是loadClass,负责加载类定义,其效果是初始化类的静态变量表,作为变量表的根节点;一个是newinstance,负责根据类的定义,生成一个类的实例变量,同时生成一个类的实例的变量表,对应于类的实例变量对象来做运行。
2.3 Method设计
Method是从Java源代码中抽象出来的java Method模型。
2.3.1 Method元素定义
2.3.2 Method分析器
从JavaClass分析结果作为入口,从其中抽取出Java方法定义。
2.3.3 Method执行器(主入口)
JavaMethod调用是SmallJavaV2.0新设计的功能,对原来的Block执行器进行二次封装实现。其具体调用执行流程如下:
定义Method的实例
定义Method执行时所需要的变量表对象,挂载到对应的类实例变量表上
在变量表上根据传入参数进行调用变量表的初始化
从Method实例中获取方法的执行体字符串
调用Block分析器进行AST分析
调用Block执行器进行执行,传入刚才定义的变量表作为调用参数;在这种情况下,变量表可以看成是Block块的执行环境。
检查获取Block执行器的返回值,作为自己的返回值返回给调用者
2.4 Block设计
2.4.1 Block元素定义
Block是程序代码的基本组成单位。Block提供了程序代码运行时程序执行流的控制功能,并不对程序代码进行实际执行,程序代码的实际执行是由Expression来完成的。各种Block元素定义如下:
按照结构化编程的理论,所有的程序结构都可以抽象为顺序、分支、循环三大类,在SmallJava中,这三种程序控制流的对应关系如下表。
2.4.2 Block模型定义
Block模型本身设计为一个高度抽象的数据模型,在具体实现时,可以基于不同的数据结构来进行具体实现。目前设计提供三种基础数据类型实现:AbstractBlock, ElementWrapper, JSONWrapper,其中的AbstractBlock和ElementWrapper已经实现。
下一步考虑将Block的模型部分再次进行抽象,抽象出一个纯粹的接口定义和底层的具体存储实现。下表是不同模型实现方式对于上面的元素定义的具体实现方式。
2.4.3 Block AST Analyse(块AST分析器)
Block 的AST分析器在设计时,采用了基于插件的设计模式。图示如下:
2.4.4 Block AST Eval(块AST执行器)
正如上文提到的,Block执行器的主要目的是驱动程序代码块,在表达式这个层次进行移动,这种情况下,需要支持顺序、条件、循环三大类的流程变动,也就是实现【控制流】。这里设计了五个Java类来实现这种控制流。
2.4.5 Block的进一步扩展
未来如果需要扩展switch()的控制流,只要再增加一个新的分析器插件,一个新的流程控制执行器即可。
未来如果需要实现中文编程,扩展使用中文关键词作为流程控制的关键词,也是按照同样的逻辑来进行处理判断和扩展即可。
2.5 Expression设计
Expression是一个标准的Java表达式解释器,在Block部分已经将大段代码进行切分以后,最终产生需要计算的最小单位,就是Expression表达式,然后Block部分把这个表达式扔给Expression部分来进行AST解析,再针对解析产生的AST进行计算。
表达式由表达式元素组合而来。
2.5.1 Expression元素定义
2.5.2 表达式AST分析
表达式的AST分析采用基于运算符号的分层切分模式来进行表达式的切分,将表达式以此切分成多个独立单元,最终构建成一个由基础元素构成的AST树出来。
这里的运算符号的查找的优先级按照如下规则来设计实现。
2.5.3 表达式AST执行器
在AST分析成功完成以后,可以按照AST树的结构,从叶子节点开始执行,并依次向上进行AST树的裁剪,直到整个AST 树全部遍历计算完毕。
这里的AST树的遍历是典型的深度优先的算法,在计算过程中将对整个AST树进行一次全面的遍历。
目前不同类型的节点的处理是使用子程序来负责完成的,下一步考虑也改造成基于插件模式的计算执行。
AST树执行以后的结果存放在根节点的一个特定变量上,供调用者进行获取调用。
2.5.4 SmallJavaV2.0优化(插件模式)
在SmallJavaV1.0中, Expression是采用了子程序的方法来进行代码切分的,这种方式不利于以后代码的扩展,因此在SmallJavaV2.0重构时,将这一部分代码按照Block分析器的模式按照插件模式进行了改造,这样在未来进行算子和算符的扩展时,只要直接增加新的插件就可以实现了。
2.6 VARTABLE设计
对于Block和Expression来说,在AST的分析阶段,是不涉及到变量表的,只要将未识别的标识符都当成变量来处理就可以了。但当Expression在计算时,必然涉及到变量的读写,这些都需要通过变量表来实现。
在SmallJava V2.0版本中,变量表被设计成为一个接口,这个接口有其具体实现。
2.6.1变量表标准接口定义
变量定义接口
public boolean defineVar(String varname, String vartype);
变量设置接口
public boolean setVarValue(String varname, VarValue varvalue) ;
变量读取接口
public VarValue getVarValue(String varname);
2.6.2变量表的具体实现
在SmallJava 1.0版本的设计中,变量表是一个完全独立的功能内,在SmallJava 2.0版本的设计中,变量表已经退化成一个标准的接口定义。
按照不同的层次性定义不同的具体实现类来实现。
2.6.3变量表的层次性
设计变量表的目的之一,是为了实现代码解释过程中遇到的作用域的问题,同一个变量名可能在Block AST 的各个层次同时存在,这个命名重复的问题,如果要让Expression 来处理,就有点太过复杂了,因此把这个复杂性封装在IVARTABLE这个接口的层次上来进行实现,当在本地的作用域,也就是本地变量表中找不到这个变量时,自动上溯到上级节点的变量表来进行查找解决,以此类推到树的根节点为止。
这部分代码在变量表的具体实现部分进行体现。
2.6.4讨论:远程变量表
VARTABLE(变量表)的设计,实际上在表达式计算时,将表达式计算使用的时间和空间进行了分割,表达式退化为纯粹的时间维度,而变量表则演化成空间维度。
目前SmallJava V2.0的变量表在设计时,仍然采用的是本地变量表的实现方式,例如使用HashMap来存储变量名和变量的具体值,如果是Java对象的化,变量中存放对于这个对象的具体引用。
但如果认真考虑变量表的实现特性时,会发现完全可以设计一个基于远程存储实现的变量表,基本类型的数据元素自不必说,可以直接以字符串的格式存放在远程存储上,例如Redis之中,而如果是Java对象的话,又分为两种情况,一种情况是这个Java对象本身支持基于字符串的序列化和拟序列化,这样的话这个对象就可以转换成字符串的格式存储在远程存储上;另一种情况,是这个对象本身不能够序列化和逆序列化,这种情况下可以考虑直接将所有的对象创建和分配工作都集中在远程的对象服务器来进行,本地的变量表只要存储一个引用关系,可以通过这个引用关系来引用到这个对象即可。但这种方式下,由于对象本身存储在远程,因此在本地的Java对象反射就无法执行了,这种情况下需要远程的对象存储上开放一个直接远程调用的接口,表达式解析器在解析到对象调用时,转换成网络远程方式来调用那个特定的远程对象即可。采用这种远程变量表和远程对象调用的话,可以很方便驱动多个不同JVM上的SmallJava解释器并行工作,因为大家要访问的变量表实际上指向了同样一个远程变量表。如下图。
2.7 CLASSTABLE设计
Java本身是一门面向对象的语言,因此SmallJava也需要对类、对象调用进行基础的支持,目前SmallJava尚不能对完整的Class定义进行全面的支持,但可以通过ClassTable来简介调用现有的Java类。
2.7.1 CLASSTABLE的核心数据结构
CLASSTABLE的核心数据结构设计为一个HashMap,key选择Class的ShortName,Value选择这个Class的Class属性。所有在Expression执行时所需要使用的类都需要首先在这个CLASSTABLE来进行注册。这一部分下一步也考虑改造成一个类似VARTABLE的接口,提供给调用者进行定制化处理。
2.7.2 CLASSTABLE中Key的使用
2.7.2.1对象定义时
所有在CLASSTABLE中注入的Key,在Expression AST解析的时候,会被识别成一个类定义,而不是一个变量,这样的语法就是合法的语法。
例如 HashMap map1; 这里的HashMap由于已经在CLASSNAME中注册,因此被识别成一个类定义,而同时,map1则被识别成一个自定义变量,其类型为HashMap.
2.7.2.2对象初始化时
对象初始化的语句,使用【new】关键词作为识别符,如果new关键词后面跟的是一个已经识别的类名称,则通过类名称在CLASSTABLE中进行查找,找到对应的Class类,然后调用Java的Class.newInstance() 方法来创建一个新实例,完成对象初始化工作。
2.7.3 对象实例调用实现
对象实例创建时,其内存分配是在JVM中的Heap区实现的,返回的是一个Java 对象引用,然后通过赋值表达式【=】的调用,Expression AST解释器将这个对象引用保存在VARTABLE中。当使用【.】操作符进行对象调用时,按照反向的顺序,先用变量名查找到内部变量,再从内部变量中拿到实际的对象引用,然后使用Java语言的反射特性查找到对应的Method指针,再通过Method指针来调用对象实例的具体方法。执行流程如下:
2.7.4 下一步设计优化
下一步针对CLASSTABLE考虑通过两方面进行优化,首先将CLASSTABLE也设计成一个标准化的接口,这样就可以根据具体情况来提供不同的实现策略;其次考虑将所有和内置对象交互的部分全部抽象到CLASSTABLE这个类来实现,而Expression AST在执行时,涉及到这部分内容时,通过调用CLASSTABLE的接口来具体实现,使得Expression AST执行器的代码更加清晰,更加内聚,它的代码仅仅负责操作自己创建的部分,而不对外部资源直接进行处理修改。
2.7.5 讨论:带参数的构造函数调用
目前SmallJava的实现有一个限制,要求所有注册的类都必须提供无参数的构造方法来供调用,未来考虑参考Spring的bean创建的方法,通过XML配置方式可以将构造函数的参数注入其中,调用时就可以调用带参数的构造函数
2.7.6讨论:自定义Class的解析和使用
目前SmallJava的CLASSTABLE只能调用现有的Java类,未来考虑增加自定义的Java类,也就是直接直接把一个Class定义字符串解释为一个Java类,在CLASSTABLE注册以后,这个类就可以和其他类一样直接进行使用。这个就类似于SmallJava解释器的自举动作了。
目前SmallJavaV2.0已经支持通过JavaFile类来加载一个自定义的Java类进来了,但目前尚未设计实现在一个Java类里面如何调用另一个自定义Java类的问题,这一问题将在后续版本升级解决。
3核心算法说明
【这一章节讲述SmallJavaV2.0最为核心的算法设计与实现】
3.1 Common公共部分
3.1.1 VarValue 类设计
VarValue设计为一个通用的数据结构,这个数据结构的作用有两个,一个是作为和变量表交互的标准输入输出对象封装,一个是作为所有表达式解析的返回值封装。
VarValue是一个VO对象,其核心代码如下:
在这里插入代码片
package com.smalljava.common;
public class VarValue {
//变量名
private String varname=null;
//变量基础类型
public String vartype=null;
//变量值,用字符串表示字符串类型的值,
//如果是对象,这里存放一个uuid,代表在对象内存表中的映射位置
public String varsvalue="";
}
3.1.2 UuidObjectManager 类设计
VarValue在设计时,使用一个String对象来尝试存储Java产生的对象,这种情况下需要设计另外一个映射关系,将Java内部的对象引用映射成一个单独的字符串,这个映射关系存储在UuidObjectManager类里面。这个类是对HashMap的一个封装。
package com.smalljava.common;
import java.util.HashMap;
/**
• MEMO:这个Manager是一个映射表,给每个应用程序创建的对象,
• MEMO:【1】 使用new 关键词创建的应用程序对象
• MEMO:【2】使用应用程序方法调用获取到的应用程序对象
• MEMO:【3】使用static方法引用返回的对象(暂时不考虑支持,未来可能支持)
• @author liujunsong
•
*/
public class UuidObjectManager {
/**
* 采用静态方法定义,保证只有这样一个对象
*/
private static HashMap<String,Object> objMap ;
public UuidObjectManager() {
//step1:init map
initMap();
}
/**
* MEMO:初始化对象存储的Map
*/
private static void initMap() {
if(objMap == null) {
objMap = new HashMap<String,Object>();
}
}
/**
* MEMO:保存一个UUID和Object的映射关系
* @param uuid
* @param obj
*/
public static void setObject(String uuid,Object obj) {
initMap();
objMap.put(uuid, obj);
}
/**
* MEMO:利用uuid来检索对象的实际存储
* @param uuid
* @return
*/
public static Object getObject(String uuid) {
initMap();
if(objMap.containsKey(uuid)) {
//利用uuid来获取指定对象
return objMap.get(uuid);
}else {
//uuid不存在
return null;
}
}
/**
* MEMO:从对象存储中删除一个对象,这样才能释放对于这个对象的具体引用
* @param uuid
*/
public static void removeObject(String uuid) {
initMap();
if(objMap.containsKey(uuid)) {
objMap.remove(uuid);
}
}
}
3.2 Space部分
3.2.1 AbastractHashMapVarTableImpl 类设计
这个类是所有变量表具体实现类的抽象基类,这个类里面实现了按照层级关系来进行逐级上溯找到合适变量的功能。
package com.smalljava.l0_space.hashmapimpl;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.smalljava.common.VarValue;
import com.smalljava.l0_space.IVarTable;
public abstract class AbstractHashMapVarTableImpl implements IVarTable {
private Logger logger = LoggerFactory.getLogger(AbstractHashMapVarTableImpl.class);
/**
* 内部的HashMap存储,这个存储目前只存储基础类型,因此使用String来存储具体数据
*/
protected HashMap<String, VarValue> myvarmap = new HashMap<String, VarValue>();
/**
* 变量表的类型
*/
private String vartabletypename = "";
/**
* 变量表的父节点的定义
*/
private AbstractHashMapVarTableImpl parentVarTable = null;
/**
* 是否是根的变量表节点
*/
private boolean rootflag = false;
/**
* 构造函数,带有类型参数
*
* @param stype
* @param parentnode
*/
public AbstractHashMapVarTableImpl(String stype, AbstractHashMapVarTableImpl parentnode) {
this.vartabletypename = stype;
this.parentVarTable = parentnode;
logger.info("init,vartabletypename:"+vartabletypename);
if (parentnode != null) {
// 设置为不是根节点
this.rootflag = false;
} else {
// 设置为根节点
this.rootflag = true;
}
}
/**
* 构造函数,带有类型参数,但不带父级节点,这样这个节点就是一个根节点了
*
* @param stype
* @param parentnode
*/
public AbstractHashMapVarTableImpl(String stype) {
this.vartabletypename = stype;
this.parentVarTable = null;
this.rootflag = true;
}
/**
* MEMO:将不带参数的构造函数隐藏掉
*/
@SuppressWarnings("unused")
private AbstractHashMapVarTableImpl() {
}
/**
* 根据varname从VarMapNode中获取数值,如果找不到,则返回null,
*
* @param varname
* @return
*/
public VarValue getVarValue(String varname) {
HashMap<String, VarValue> varmap = this.myvarmap;
if (varmap == null) {
logger.error("【ERROR】查找变量表失败.");
return null;
}
AbstractHashMapVarTableImpl pvartable = this;
// 循环开始
while (pvartable.getMyvarmap() != null) {
varmap = pvartable.getMyvarmap();
if (varmap.containsKey(varname)) {
// 如果varname被定义为一个本地变量,则从本地HashMap中获取其值
VarValue varvalue = varmap.get(varname);
return varvalue;
}
//指向父节点
if(pvartable.getParentVarTable()!=null) {
pvartable = pvartable.getParentVarTable();
}else {
//跳出循环
break;
}
}
// 循环查找,指到找不到varname,则返回null代表找不到这个变量
return null;
}
/**
* 变量赋值,首先找到这个变量,然后再赋值
*
* @param varname
* @param varvalue
* @return
*/
public boolean setVarValue(String varname, VarValue varvalue) {
HashMap<String, VarValue> varmap = this.myvarmap;
if (varmap == null) {
logger.error("查找变量表失败.");
return false;
}
if (varname == null || varname.length() == 0) {
logger.error("setVarValue程序代码有误,变量名为空或者为空字符串");
return false;
}
if (varvalue == null) {
logger.error("setVarValue程序代码有误,变量设置参数为null");
return false;
}
VarValue vvalue = this.getVarValue(varname);
if (vvalue == null) {
logger.error("setVarValue程序执行有误,没有找到变量定义:" + varname);
return false;
} else {
logger.info("从变量表中查找变量成功."+varname);
vvalue.setVarname(varname);
vvalue.setVartype(varvalue.getVartype());
vvalue.setVarsvalue(varvalue.getVarsvalue());
return true;
}
}
/**
* 【变量设置】算法描述:先找到第一个可用的变量表,在这个变量表中定义变量值
*
* @param varname
* @param vartype
* @return
*/
public boolean defineVar(String varname, String vartype) {
HashMap<String, VarValue> varmap = this.myvarmap;
if (varmap == null) {
logger.error("查找变量表失败.");
return false;
}
if (varmap.containsKey(varname)) {
logger.error("程序代码有误,此处有重复定义的变量名:" + varname);
return false;
}
if (vartype == null) {
logger.error("程序代码有误,变量定义时类型为空." + varname);
return false;
}
VarValue varvalue = new VarValue();
varvalue.setVarname(varname);
varvalue.setVartype(vartype);
if (vartype.equals("int") || vartype.equals("long") || vartype.equals("float") || vartype.equals("double")) {
varvalue.setVarsvalue("0");
} else if (vartype.equals("boolean")) {
varvalue.setVarsvalue("false");
} else if (vartype.equals("String")) {
varvalue.setVarsvalue("");
} else {
// 类型为对象时,svalue代表映射地址,""就代表是一个null值
varvalue.setVarsvalue("");
}
//写入当前节点的变量表
varmap.put(varname, varvalue);
return true;
}
/**
* MEMO:判断是否是有效变量
*/
@Override
public boolean isValid(String varname) {
VarValue vvalue = this.getVarValue(varname);
if (vvalue == null) {
return false;
} else {
return true;
}
}
public HashMap<String, VarValue> getMyvarmap() {
return myvarmap;
}
public AbstractHashMapVarTableImpl getParentVarTable() {
return parentVarTable;
}
public void setParentVarTable(AbstractHashMapVarTableImpl parentVarTable) {
this.parentVarTable = parentVarTable;
}
public boolean isRootflag() {
return rootflag;
}
public void setRootflag(boolean rootflag) {
this.rootflag = rootflag;
}
@Override
public String toJSONString() {
String s1=JSON.toJSONString(this.myvarmap);
return s1;
}
}
3.3 JavaFile部分
3.3.1 JavaFileAnalyse 类设计
package com.smalljava.l1_javafile.analyse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.StringFindUtil;
import com.smalljava.l1_javafile.analyse.plugin.IJavaFileAnalysePlugin;
import com.smalljava.l1_javafile.analyse.plugin.JavaFileAnalysePluginManager;
import com.smalljava.l1_javafile.vo.AbstractJavaFileElement;
import com.smalljava.l1_javafile.vo.JavaFileRootVO;
public class JavaFileAnalyse {
private Logger logger = LoggerFactory.getLogger(JavaFileAnalyse.class);
/**
* 字符串解析算法,输入一个代表Java源文件的字符串,解析成一个JavaFileRootVO对象
* @param stringcontent
* @return
*/
@SuppressWarnings("static-access")
public JavaFileRootVO analyse(String strcontent) {
if(strcontent==null ) {
logger.info("【ERROR】strcontent is null.");
return null;
}
StringFindUtil util = new StringFindUtil();
strcontent = util.trimReturnAndSpace(strcontent);
if(strcontent.equals("")) {
logger.info("【ERROR】strcontent is empty.");
return null;
}
JavaFileAnalysePluginManager manager = new JavaFileAnalysePluginManager();
JavaFileRootVO rootvo = new JavaFileRootVO();
while(strcontent.length()>0) {
strcontent = util.trimReturnAndSpace(strcontent);
//循环调用分析器
boolean loopflag = false;
for(IJavaFileAnalysePlugin plugin : manager.getPluginarray()) {
AbstractJavaFileElement newele = plugin.findFirstElement(strcontent);
if(newele == null) {
continue;
}else {
loopflag = true;
rootvo.getChildren().add(newele);
strcontent = newele.getComputeleftstring();
//跳出此次循环
break;
}
}
if(loopflag) {
//继续外部循环
continue;
}else {
logger.error("【ERROR】调用插件解析失败:"+strcontent);
return null;
}
}
//返回循环调用结果
return rootvo;
}
}
3.4 JavaClass部分
3.4.1 JavaClassAnalyse 类设计
package com.smalljava.l2_class.analyse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.StringFindUtil;
import com.smalljava.l1_javafile.vo.element.JavaFileClassElement;
import com.smalljava.l2_class.analyse.plugin.IJavaClassAnalysePlugin;
import com.smalljava.l2_class.analyse.plugin.JavaClassAnalysePluginManager;
import com.smalljava.l2_class.vo.JavaClassTemplateVO;
import com.smalljava.l2_class.vo.element.AbstractJavaClassElement;
/**
- 这个类负责将一个class定义的字符串分解成一个class的VO对象
- @author liujunsong
*/
public class JavaClassAnalyse {
private Logger logger = LoggerFactory.getLogger(JavaClassAnalyse.class);
@SuppressWarnings("static-access")
public JavaClassTemplateVO analyse(JavaFileClassElement element) {
if(element==null) {
logger.error("[ArgumentErro] element is null.");
}
String strcontent = element.getStringcontent();
if(strcontent==null ) {
logger.error("【ERROR】strcontent is null.");
return null;
}
JavaClassTemplateVO rootvo = new JavaClassTemplateVO();
StringFindUtil util = new StringFindUtil();
strcontent = util.trimReturnAndSpace(strcontent);
//如果strcontent被{}包裹,则去掉这个包裹
if(strcontent.startsWith("{") && strcontent.endsWith("}")) {
strcontent=strcontent.substring(1,strcontent.length()-1);
strcontent = util.trimReturnAndSpace(strcontent);
}
if(strcontent.equals("")) {
logger.debug("【INFO】strcontent is empty.");
return rootvo;
}
JavaClassAnalysePluginManager manager = new JavaClassAnalysePluginManager();
while(strcontent.length()>0) {
strcontent = util.trimReturnAndSpace(strcontent);
//循环调用分析器,先设置循环的执行状态标志
boolean loopflag = false;
for(IJavaClassAnalysePlugin plugin : manager.getPluginarray()) {
//从字符串里面解析新元素出来
AbstractJavaClassElement newele = plugin.findFirstElement(strcontent);
if(newele == null) {
continue;
}else {
loopflag = true;
rootvo.getChildren().add(newele);
strcontent = newele.getComputeleftstring();
//跳出此次循环
break;
}
}
if(loopflag) {
//各个插件找到了一个元素继续外部循环
continue;
}else {
logger.error("【ERROR】调用插件解析失败:"+strcontent);
return null;
}
}
//返回循环调用结果
return rootvo;
}
}
3.4.2 JavaClassEval 类设计
package com.smalljava.l2_class.eval;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.StringFindUtil;
import com.smalljava.l0_space.IClassTable;
import com.smalljava.l0_space.JavaClassStaticInstanceMap;
import com.smalljava.l0_space.hashmapimpl.L2_HashMapClassInstanceVarTableImpl;
import com.smalljava.l0_space.hashmapimpl.L2_HashMapClassStaticVarTableImpl;
import com.smalljava.l2_class.vo.JavaClassInstanceVO;
import com.smalljava.l2_class.vo.JavaClassStaticInstanceVO;
//import com.smalljava.commonspace.IVarTable;
import com.smalljava.l2_class.vo.JavaClassTemplateVO;
import com.smalljava.l2_class.vo.element.JavaClassMethodElement;
//import com.smalljava.l2_class.vo.element.JavaClassMethodElement;
import com.smalljava.l2_class.vo.element.JavaClassVarDefineElement;
import com.smalljava.l4_block.blockanalyse.BlockAnalyse;
import com.smalljava.l4_block.blockeval.BlockEvaluator;
import com.smalljava.l4_block.blockvo.BasicBlock;
public class JavaClassEval {
private static Logger logger = LoggerFactory.getLogger(JavaClassEval.class);
/**
* MEMO:加载Class对象定义 MEMO:输出结果:在全局的JavaClassStaticInstanceMap中增加新对象
*
* @param javafilestring 代表Java源代码的字符串
* @param javaclass 代表其中的java class名称
*/
public boolean loadClass(JavaClassTemplateVO rootvo, IClassTable classtable) {
if (rootvo == null) {
logger.error("[Arugment Error]rootvo is null.");
return false;
} else {
logger.info("[Argument OK]rootvo is not null.");
}
if (classtable == null) {
logger.error("[Arugment Error]classtable is null.");
return false;
}
// 增加一个额外判断,如果这个类已经被加载过,则不需要重复加载了。
String classname = rootvo.getClassname();
if (JavaClassStaticInstanceMap.getJavaClassStaticInstanceVO(classname) != null) {
logger.error("[INFO] Class has been loaded.");
return true;
}
// 在类里面查找变量定义的部分代码,并尝试进行变量定义。
// rootvo本身实现了变量表的功能,因此可以转型成变量表来使用。
// 构造函数暂时不考虑支持。
// IVarTable vartable = rootvo;
String classinitblock = "";
for (JavaClassVarDefineElement element : rootvo.getPropertiesArray()) {
logger.info(element.getStringcontent());
// 不是对所有的属性定义都进行处理
// 需要判断这个变量定义是否包含static关键词
if (element.getStringcontent().indexOf(" static ") > 0) {
// 这里需要去掉public,private,static 这些前缀定义
// public,private这两种访问控制暂时不考虑实现
String strcode = element.getStringcontent();
// TODO:需要规避在引号之内的情况
// 此处暂时使用简易算法
StringFindUtil util = new StringFindUtil();
strcode = util.trimReturnAndSpace(strcode);
if (strcode.startsWith("public")) {
strcode = strcode.substring("public".length());
strcode = util.trimReturnAndSpace(strcode);
}
if (strcode.startsWith("private")) {
strcode = strcode.substring("private".length());
strcode = util.trimReturnAndSpace(strcode);
}
if (strcode.startsWith("static")) {
strcode = strcode.substring("static".length());
strcode = util.trimReturnAndSpace(strcode);
}
// 现在这个代码是干净的代码了
logger.info(strcode);
classinitblock += strcode;
}
}
// 利用rootvo作为输入,构建另外一个JavaClassStaticInstanceVO对象
JavaClassStaticInstanceVO staticvo = this.getJavaClassStaticInstanceVO(rootvo);
// 构建根级别的变量表对象
L2_HashMapClassStaticVarTableImpl staticvartable = new L2_HashMapClassStaticVarTableImpl("");
// 将静态实例对象和静态变量表关联起来。
// 这个方法似乎可以封装在staticvo里面
staticvo.setVartable(staticvartable);
// TODO:调用level3包里面封装的评估方法来进行评估计算
BasicBlock closedblock = new BasicBlock("", classinitblock, null);
// 设置变量表,这个块就是class本身的变量表
// TODO:设置变量计算的变量表
//closedblock.setMyvarmap(staticvo.getMyvarmap());
//closedblock.setVartable(staticvo.getVartable());
BlockAnalyse ba = new BlockAnalyse();
boolean isok = ba.analyse(closedblock);
closedblock.show(0);
logger.info("代码块分析结果:" + isok);
BlockEvaluator node = new BlockEvaluator();
L2_HashMapClassStaticVarTableImpl vartable1 = new L2_HashMapClassStaticVarTableImpl("");
boolean b2;
try {
b2 = node.execute(closedblock, vartable1, classtable);
if (b2) {
// 执行成功的情况下,将静态实例写入全局存储中
JavaClassStaticInstanceMap.add(staticvo);
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* MEMO:根据类定义的模板对象,生成一个新的实例对象出来 MEMO:输出结果:在全局的JavaClassStaticInstanceMap中增加新对象
*
* @param javafilestring 代表Java源代码的字符串
* @param javaclass 代表其中的java class名称
*/
public JavaClassInstanceVO newInstance(JavaClassTemplateVO rootvo, IClassTable classtable) {
if (rootvo == null) {
logger.error("[Arugment Error]rootvo is null.");
return null;
} else {
logger.info("[Argument OK]rootvo is not null.");
}
if (classtable == null) {
logger.error("[Arugment Error]classtable is null.");
return null;
}
// 在类里面查找变量定义的部分代码,并尝试进行变量定义。
// rootvo本身实现了变量表的功能,因此可以转型成变量表来使用。
// 构造函数暂时不考虑支持。
// IVarTable vartable = rootvo;
String classinitblock = "";
for (JavaClassVarDefineElement element : rootvo.getPropertiesArray()) {
logger.info(element.getStringcontent());
// 不是对所有的属性定义都进行处理
// 需要判断这个变量定义是否包含static关键词
if (element.getStringcontent().indexOf("static ") < 0) {
// 这里需要去掉public,private,static 这些前缀定义
// public,private这两种访问控制暂时不考虑实现
String strcode = element.getStringcontent();
// TODO:需要规避在引号之内的情况
// 此处暂时使用简易算法
StringFindUtil util = new StringFindUtil();
strcode = util.trimReturnAndSpace(strcode);
if (strcode.startsWith("public")) {
strcode = strcode.substring("public".length());
strcode = util.trimReturnAndSpace(strcode);
}
if (strcode.startsWith("private")) {
strcode = strcode.substring("private".length());
strcode = util.trimReturnAndSpace(strcode);
}
if (strcode.startsWith("static")) {
strcode = strcode.substring("static".length());
strcode = util.trimReturnAndSpace(strcode);
}
// 现在这个代码是干净的代码了
logger.info(strcode);
classinitblock += strcode;
}
}
// 利用rootvo作为输入,构建另外一个JavaClassStaticInstanceVO对象
JavaClassInstanceVO instancevo = this.getJavaClassInstanceVO(rootvo);
// TODO:调用level3包里面封装的评估方法来进行评估计算
BasicBlock closedblock = new BasicBlock("", classinitblock, null);
// 设置变量表,这个块就是class本身的变量表
//closedblock.setMyvarmap(instancevo.getMyvarmap());
//TODO:设置变量表
//closedblock.setVartable(instancevo.getVarTable()));
BlockAnalyse ba = new BlockAnalyse();
boolean isok = ba.analyse(closedblock);
closedblock.show(0);
logger.info("代码块分析结果:" + isok);
BlockEvaluator node = new BlockEvaluator();
L2_HashMapClassStaticVarTableImpl vartable1 = new L2_HashMapClassStaticVarTableImpl("");
L2_HashMapClassInstanceVarTableImpl vartable2 = new L2_HashMapClassInstanceVarTableImpl("",vartable1);
boolean b2;
try {
b2 = node.execute(closedblock,vartable2, classtable);
if (b2) {
return instancevo;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* MEMO:根据模板来生成类静态实例对象
* @param rootvo
* @return
*/
private JavaClassStaticInstanceVO getJavaClassStaticInstanceVO(JavaClassTemplateVO rootvo) {
JavaClassStaticInstanceVO staticInstanceVO = new JavaClassStaticInstanceVO();
staticInstanceVO.setClassname(rootvo.getClassname());
staticInstanceVO.setPackagename(rootvo.getPackagename());
// staticInstanceVO的变量表将在下一步设定
// staticInstanceVO暂时不考虑支持static Method定义(略微复杂)
return staticInstanceVO;
}
/**
* MEMO:根据模板来生成类的实例对象
* @param rootvo
* @return
*/
private JavaClassInstanceVO getJavaClassInstanceVO(JavaClassTemplateVO rootvo) {
JavaClassInstanceVO instanceVO = new JavaClassInstanceVO();
instanceVO.setClassname(rootvo.getClassname());
instanceVO.setPackagename(rootvo.getPackagename());
// staticInstanceVO的变量表将在下一步设定
// staticInstanceVO暂时不考虑支持static Method定义(略微复杂)
for (JavaClassMethodElement method : rootvo.getMethodArray()) {
JavaClassMethodElement method2 = new JavaClassMethodElement();
method2.setStringcontent(method.getStringcontent());
instanceVO.getChildren().add(method2);
}
return instanceVO;
}
}
3.5 Method部分
3.5.1 JavaMethodAnalyse 类设计
package com.smalljava.l3_method.analyse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.StringFindUtil;
import com.smalljava.l2_class.vo.element.JavaClassMethodElement;
import com.smalljava.l3_method.vo.JavaMethodArgumentVO;
import com.smalljava.l3_method.vo.JavaMethodRootVO;
/**
- JAVA的method方法分析器
- @author liujunsong
*/
public class JavaMethodAnalyse {
private Logger logger = LoggerFactory.getLogger(JavaMethodAnalyse.class);
/**
* 将一个方法的定义字符串解析成名称,参数,内容三部分
* @param strcontent
* @return
*/
public JavaMethodRootVO analyse(JavaClassMethodElement methodelement) {
if(methodelement==null ) {
logger.info("【ERROR】strcontent is null.");
return null;
}
String strcontent = methodelement.getStringcontent();
if(strcontent == null) {
logger.error("【ERROR】strcontent is null.");
return null;
}else {
logger.debug("method content:"+strcontent);
}
StringFindUtil util = new StringFindUtil();
strcontent = util.trimReturnAndSpace(strcontent);
if(strcontent.equals("")) {
logger.error("【ERROR】strcontent is emtyp.");
return null;
}
if(! strcontent.endsWith("}")) {
logger.error("【ERROR】strcontent is not ended by 【}】.");
return null;
}
JavaMethodRootVO rootvo = new JavaMethodRootVO();
//TODO:从其中抽取方法名,变量参数,方法名定义
int ipos1 = util.findfirstStringForBlock(strcontent, "{");
if(ipos1<0) {
logger.error("【ERROR】strcontent cannot find 【{】.");
return null;
}
//设置Method对象的字符串内容
String methodcontent = strcontent.substring(ipos1);
rootvo.setMethodContent(methodcontent);
int ipos2 = strcontent.indexOf("(");
int ipos3 = strcontent.indexOf(")");
if(ipos2<0 || ipos3<0) {
logger.error("【ERROR】strcontent 查找()失败!"+strcontent);
return null;
}
if(ipos2>ipos3) {
logger.error("【ERROR】strcontent ipos2>ipos3");
return null;
}
String argdefine = strcontent.substring(ipos2+1,ipos3);
String args[] = argdefine.split(",");
for(String arg:args) {
//按照空格来分解arg
arg = arg.trim();
if(arg.length()==0) {
//空字符串跳过循环
continue;
}
String argvalue[] = arg.split(" ");
if(argvalue.length !=2) {
logger.error("【ERROR】Method arg analyse error:"+arg);
return null;
}else {
JavaMethodArgumentVO argvo = new JavaMethodArgumentVO();
argvo.setArgtype(argvalue[0]);
argvo.setArgname(argvalue[1]);
rootvo.getArgArray().add(argvo);
}
}
String leftdata = strcontent.substring(0,ipos2);
leftdata = util.trimReturnAndSpace(leftdata);
String sdata[] = leftdata.split(" ");
//最后一个字符串是方法名
if(sdata.length>0) {
rootvo.setMethodname(sdata[sdata.length-1]);
}else {
logger.error("【ERROR】 leftdata is empty."+leftdata);
return null;
}
return rootvo;
}
}
3.5.2 JavaMethodEval 类设计
package com.smalljava.l3_method.eval;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.VarValue;
import com.smalljava.l0_space.IClassTable;
import com.smalljava.l0_space.IVarTable;
import com.smalljava.l0_space.hashmapimpl.L2_HashMapClassInstanceVarTableImpl;
import com.smalljava.l0_space.hashmapimpl.L2_HashMapClassStaticVarTableImpl;
import com.smalljava.l0_space.hashmapimpl.L3_HashMapMethodInstanceVarTableImpl;
import com.smalljava.l3_method.vo.JavaMethodRootVO;
import com.smalljava.l4_block.blockanalyse.BlockAnalyse;
import com.smalljava.l4_block.blockeval.BlockEvaluator;
import com.smalljava.l4_block.blockvo.BasicBlock;
public class JavaMethodEval {
private Logger logger = LoggerFactory.getLogger(JavaMethodEval.class);
/**
* MEMO:针对Method的方法调用,需要传入IVarTable,ClassTable
* MEMO:先不考虑调用参数的问题
* MEMO:Method块需要把调用参数加入到自己的变量表里面去,并引用对象的变量表
* @param methodvo
* @param vartable
* @return
*/
public VarValue eval(JavaMethodRootVO methodvo,IVarTable classvartable,IClassTable classtable) {
if(methodvo == null) {
logger.error("methodvo is null.");
return null;
}
BasicBlock closedblock = new BasicBlock("",methodvo.getMethodContent(),null);
//closedblock.setClassVarTable(classvartable);
BlockAnalyse ba = new BlockAnalyse();
boolean isok = ba.analyse(closedblock);
closedblock.show(0);
logger.info("代码块分析结果:"+isok);
BlockEvaluator eval = new BlockEvaluator();
L2_HashMapClassStaticVarTableImpl vartable1 = new L2_HashMapClassStaticVarTableImpl("");
L2_HashMapClassInstanceVarTableImpl vartable2 = new L2_HashMapClassInstanceVarTableImpl("",vartable1);
L3_HashMapMethodInstanceVarTableImpl vartable3 = new L3_HashMapMethodInstanceVarTableImpl("",vartable2);
boolean b2;
try {
b2 = eval.execute(closedblock,vartable3,classtable);
if(b2) {
//TODO:此处需要查找并设计返回值
logger.info("Method call finished ok.");
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return null;
}
}
3.6 Block部分
3.6.1 BlockAnalyse 类设计
package com.smalljava.l4_block.blockanalyse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.l4_block.SmallJavaBlockConst;
import com.smalljava.l4_block.blockvo.BasicBlock;
import com.smalljava.l4_block.blockvo.childblock.MethodBlock;
/**
•
• @author liujunsong
•
*/
public class BlockAnalyse {
private Logger logger = LoggerFactory.getLogger(BlockAnalyse.class);
private BlockAnalysePluginManager pmanager = new BlockAnalysePluginManager();
/**
* 构造函数
*/
public BlockAnalyse() {
}
/**
* 把字符串rootblock.computestring分解成AbastractBlock,里面用Children来构造成树状结构
*
* @return ture 分解成功 false 分解失败
*/
public boolean analyse(BasicBlock rootblock) {
// step1:检查输入参数
if (rootblock == null) {
logger.error("Argument Error,rootblock is null.");
return false;
}
if (rootblock.getBlockContent() == null) {
logger.error("Argument Error,rootblock.computestring is null.");
return false;
}
// 如果是MethodBlock,停止分析
if (rootblock instanceof MethodBlock) {
logger.error("Method block stop analyse.");
return true;
}
//判断rootblock的类型
if(rootblock.getBlocktype()!=null
&& rootblock.getBlocktype().equals(SmallJavaBlockConst.ImportBlock)) {
logger.error("import 语句不再继续处理。");
return true;
}
if(rootblock.getBlocktype()!=null
&& rootblock.getBlocktype().equals(SmallJavaBlockConst.SingleLineMemo)) {
logger.error("singlelinememo 语句不再继续处理。");
return true;
}
if(rootblock.getBlocktype()!=null
&& rootblock.getBlocktype().equals(SmallJavaBlockConst.MultiLineMemo)) {
logger.error("multilinememo 语句不再继续处理。");
return true;
}
// 初始化计算参数
rootblock.computestring = rootblock.getBlockContent();
logger.info("---->analyse begin." + rootblock.computestring);
// step2.写入rootblock,仅做备份用
rootblock.computestring = this._trimReturnAndSpace(rootblock.computestring);
rootblock.setBlockContent(rootblock.computestring);
// step3.开始处理非循环部分
if (rootblock.computestring.equals("") && rootblock.getChildren().size()==0) {
rootblock.setBlocktype(SmallJavaBlockConst.EmptyBlock);
return true;
}
// 循环处理子节点时,需要跳过IfBlock,直接处理其下级
if (!rootblock.getBlocktype().equals(SmallJavaBlockConst.Ifblock)) {
// Step4.开始进行循环判断处理,根据首字母来进行判断走下一步的分支,
// 每次执行完毕后堆rootblock.computestring进行切分
while (rootblock.computestring.length() > 0) {
// Step4.0,进行trim处理
rootblock.computestring = this._trimReturnAndSpace(rootblock.computestring);
// 循环调用插件进行处理,如果插件返回false,说明插件出错了,停止执行
String s1 = rootblock.computestring;
if (!pmanager.process(rootblock)) {
logger.info("【ERROR】解析插件调用失败:" + rootblock.computestring);
// 调用失败,返回false
return false;
}
String s2 = rootblock.computestring;
if (s1.length() == s2.length()) {
// 字符串没有得到有效处理的情况下,返回false
// 各个插件之中逻辑上必须有匹配的才对
logger.info("【ERROR】所有的插件执行以后,字符串没有得到任何处理." + rootblock.computestring);
return false;
}
}
}
// Step5.本级节点切分完毕,开始处理下级节点,并判断其返回值
for (BasicBlock child : rootblock.getChildren()) {
//此处需要根据child的类型来判断是否需要继续
if(child.getBlocktype().equals(SmallJavaBlockConst.SingleLineMemo)) {
continue;
}
if(child.getBlocktype().equals(SmallJavaBlockConst.MultiLineMemo)) {
continue;
}
if(child.getBlocktype().equals(SmallJavaBlockConst.ImportBlock)) {
continue;
}
if (!analyse(child)) {
logger.info("【ERROR】递归调用子节点分析时发生错误.");
return false;
}
}
// 程序成功运行完毕
return true;
}
/**
* 将字符串开始和结束位置的\r\n ,\r,空格都过滤掉
*
* @param strinput
* @return
*/
private String _trimReturnAndSpace(String strinput) {
// String sout = "";
// 先查找第一个不是\r\n \r 空格的位置
int ipos = -1;
for (int i = 0; i < strinput.length(); i++) {
if (strinput.charAt(i) == '\r' || strinput.charAt(i) == '\n' || strinput.charAt(i) == ' ') {
// 继续循环
continue;
} else {
ipos = i;
break;
}
}
if (ipos == -1) {
// 没有找到有效字符
return "";
}
// 开始从后往前查找第一个有效字符
int ipos2 = -1;
for (int i = strinput.length() - 1; i >= 0; i--) {
if (strinput.charAt(i) == '\r' || strinput.charAt(i) == '\n' || strinput.charAt(i) == ' ') {
// 继续循环
continue;
} else {
ipos2 = i;
break;
}
}
// 由于ipos有效,所以ipos2一定也是有效的
if (ipos2 >= ipos) {
return strinput.substring(ipos, ipos2 + 1);
} else {
logger.error("程序执行出现错误,需要查找问题所在.ipos,ipos2=" + ipos + "," + ipos2);
return "";
}
}
}
3.6.2 BlockEvaluator 类设计
package com.smalljava.l4_block.blockeval;
import java.util.ArrayList;
import java.util.List;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.VarValue;
import com.smalljava.l0_space.IClassTable;
//import com.smalljava.l0_space.IVarTable;
import com.smalljava.l0_space.hashmapimpl.AbstractHashMapVarTableImpl;
import com.smalljava.l0_space.hashmapimpl.L4_HashMapBlockVarTableImpl;
import com.smalljava.l4_block.SmallJavaBlockConst;
import com.smalljava.l4_block.blockvo.BasicBlock;
import com.smalljava.l4_block.blockvo.childblock.DOWHILEBlock;
import com.smalljava.l4_block.blockvo.childblock.FORBlock;
import com.smalljava.l4_block.blockvo.childblock.IFBlock;
import com.smalljava.l4_block.blockvo.childblock.MethodBlock;
import com.smalljava.l4_block.blockvo.childblock.WHILEBlock;
import com.smalljava.l5_expression.analyse.ExpressionASTAnalyse;
import com.smalljava.l5_expression.eval.ExpressionEval;
import com.smalljava.l5_expression.vo.RootAST;
public class BlockEvaluator {
private Logger logger = LoggerFactory.getLogger(BlockEvaluator.class);
// 本地的变量表
//
// 默认的构造函数
public BlockEvaluator() {
}
/**
* 对rootblock进行计算评估
*
* @param child2
* @return
* @throws Exception
*/
@SuppressWarnings("static-access")
public boolean execute(BasicBlock child2,AbstractHashMapVarTableImpl vartable, IClassTable classtable) throws Exception {
// 首先判断这个node有没有child
if (child2.getChildren() == null || child2.getChildren().size() == 0) {
// 空节点不用返回
if (child2.getBlockContent() != null && child2.getBlockContent().equals("")) {
logger.info("block计算,空节点不用计算");
return true;
}
// 备注节点不用处理,直接返回
if (child2.getBlocktype() != null
&& child2.getBlocktype().equals(SmallJavaBlockConst.SingleLineMemo)) {
logger.info("备注节点不用计算");
return true;
}
// 多行备注节点不用处理,直接返回
if (child2.getBlocktype() != null
&& child2.getBlocktype().equals(SmallJavaBlockConst.MultiLineMemo)) {
logger.info("多行备注节点不用计算");
return true;
}
// 这是一个没有下级节点的节点,已经是最底层的节点了
// 把这个节点转换成一个ASTTree
//ASTTreeNode node = new ASTTreeNode(child2.getBlockContent(), 0);
ExpressionASTAnalyse expanalyse = new ExpressionASTAnalyse();
RootAST node = expanalyse.analyse(child2.getBlockContent());
if(node==null) {
logger.info("表达式解析失败."+child2.getBlockContent());
return false;
}else {
logger.info("表达式解析成功:"+child2.getBlockContent());
}
node.show(0);
//ClassTable classtable = new ClassTable();
//boolean b3 = node.eval(child2,classtable);
ExpressionEval expressioneval = new ExpressionEval();
VarValue b3 = expressioneval.eval(node, vartable, classtable);
logger.info("eval [" + node.getStrexpression() + "]计算结果:" + b3);
if (b3 != null) {
logger.error("计算结果:" + b3.toString());
return true;
} else {
logger.error("计算失败!" + node.getStrexpression());
return false;
}
}
// 如果是BaseBlockNode的节点类型,那么就顺序执行各子节点
// 其他不同类型的节点的执行方法,在对应类型的节点中进行定义
for (BasicBlock child : child2.getChildren()) {
// child.setParent(this);
// child.execute();
// 如果child是 MethodBlock,则退出执行
if(child instanceof MethodBlock) {
//MethodBlock不可以作为一个children来执行
//需要作为一个主节点来调用
//MethodBlock作为一个Child的时候不可执行
continue;
}
if (child instanceof DOWHILEBlock) {
DOWHILEBlockEvaluator doblockeval = new DOWHILEBlockEvaluator();
L4_HashMapBlockVarTableImpl vartable1 = new L4_HashMapBlockVarTableImpl("",vartable);
boolean b1 = doblockeval.execute((DOWHILEBlock) child,vartable1,classtable);
logger.info("doblock 计算结果:" + b1);
if (b1) {
// 子节点执行成功,继续
continue;
} else {
return false;
}
}
if (child instanceof WHILEBlock) {
WHILEBlockEvaluator whileblockeval = new WHILEBlockEvaluator();
L4_HashMapBlockVarTableImpl vartable2 = new L4_HashMapBlockVarTableImpl("",vartable);
boolean b2 = whileblockeval.execute((WHILEBlock) child,vartable2,classtable);
logger.info("while block 计算结果:" + b2);
if (b2) {
continue;
} else {
return false;
}
}
if (child instanceof IFBlock) {
IFBlockEvaluator ifeval = new IFBlockEvaluator();
L4_HashMapBlockVarTableImpl vartable3 = new L4_HashMapBlockVarTableImpl("",vartable);
boolean b3 = ifeval.execute((IFBlock) child,vartable3,classtable);
logger.info("if block 计算结果" + b3);
if (b3) {
continue;
} else {
return false;
}
}
if (child instanceof FORBlock) {
FORBlockEvaluator foreval = new FORBlockEvaluator();
L4_HashMapBlockVarTableImpl vartable4 = new L4_HashMapBlockVarTableImpl("",vartable);
boolean b4 = foreval.execute((FORBlock) child,vartable4,classtable);
logger.info("for block 计算结果" + b4);
if (b4) {
continue;
} else {
return false;
}
}
//暂时假设所有child使用统一的变量表
if (execute(child, vartable, classtable)) {
continue;
} else {
return false;
}
}
return true;
}
@SuppressWarnings("unused")
private List<Element> getChildren(Element root){
@SuppressWarnings("rawtypes")
List list1 = root.elements();
List<Element> retlist = new ArrayList<Element>();
//从List里面删除VARTABLE节点
for(Object obj:list1) {
if(obj instanceof Element) {
Element element = (Element)obj;
if(! element.getName().equals("VATTABLE")) {
retlist.add(element);
}
}
}
return retlist;
}
}
3.7 Expression部分
3.7.1 ExpressionASTAnalyse 类设计
package com.smalljava.l5_expression.analyse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.l5_expression.vo.AbstractLeafElement;
import com.smalljava.l5_expression.vo.MiddleAST;
import com.smalljava.l5_expression.vo.RootAST;
public class ExpressionASTAnalyse {
private static Logger logger = LoggerFactory.getLogger(ExpressionASTAnalyse.class);
/**
* MEMO:输入一个字符串,分析以后得到一个树状结构
* @param strexpresion
* @return
*/
@SuppressWarnings("static-access")
public static RootAST analyse(String strexpresion) {
ExpressionASTPluginManager manager = new ExpressionASTPluginManager();
//循环调用各个插件,检查其返回结果
RootAST root = null;
for(IAstPlugin plugin: manager.getPluginmap()) {
logger.debug("call "+plugin.getClass().getSimpleName());
root = (RootAST) plugin.analyse(strexpresion);
if(root !=null) {
//终止循环
logger.info("plugin OK.");
break;
}
}
if(root == null) {
logger.error("【ERROR】所有插件均已经执行完毕,分析失败!"+strexpresion);
return null;
}else {
//开始循环调用其children
for(RootAST child: root.getChildren()) {
//跳过child本身是leaf节点的类型
//这类子节点不再递归调用
if(child instanceof AbstractLeafElement) {
continue;
}
//如果这个child本身是RootAST
if(child.getClass().getName().equals(MiddleAST.class.getName())) {
RootAST newchild = ExpressionASTAnalyse.analyse(child.getStrexpression());
if(newchild==null) {
logger.error("子节点解析失败,请检查有无合适插件支持!【"+child.getStrexpression()+"】");
return null;
}else {
child.getChildren().add(newchild);
}
}
}
return root;
}
}
@SuppressWarnings("static-access")
public static void main(String args[]) {
String s1="1+2+3+4+5+6+7+8";
ExpressionASTAnalyse ea = new ExpressionASTAnalyse();
RootAST ast = ea.analyse(s1);
if(ast!=null) {
logger.debug("---->分析成功:"+ast.getUuid());
ast.show(0);
}else {
logger.error("---->分析失败!【"+s1+"】");
}
}
}
3.7.2 ExpressionEval 类设计
package com.smalljava.l5_expression.eval;
import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.smalljava.common.VarValue;
import com.smalljava.l0_space.IClassTable;
import com.smalljava.l0_space.IVarTable;
import com.smalljava.l5_expression.eval.plugin.atom.AtomEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.constvalue.ConstEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.one.LogicNotPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicAndOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicEqualsOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicGreaterEqualOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicGreaterOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicLitterEqualOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicLitterOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicNotEqualOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.LogicOrOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.MathAddOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.MathDeAddOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.MathDevideOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.two.MathMultiOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.var.DefineOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.var.NewOperEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.var.VarSetEvalPlugin;
import com.smalljava.l5_expression.eval.plugin.var.VariableEvalPlugin;
import com.smalljava.l5_expression.vo.MiddleAST;
import com.smalljava.l5_expression.vo.RootAST;
import com.smalljava.l5_expression.vo.atom.AtomElement;
import com.smalljava.l5_expression.vo.constvalue.AbstractConstDataElement;
import com.smalljava.l5_expression.vo.one.LogicNotOperElement;
import com.smalljava.l5_expression.vo.two.DualOperDataOperElement;
import com.smalljava.l5_expression.vo.var.NewOperElement;
import com.smalljava.l5_expression.vo.var.VarDataElement;
import com.smalljava.l5_expression.vo.var.VarDefineOperElement;
import com.smalljava.l5_expression.vo.var.VarSetOperElement;
public class ExpressionEval implements IExpressionEval {
private Logger logger = LoggerFactory.getLogger(ExpressionEval.class);
private static HashMap<String, IExpressionEval> evalmap = new HashMap<String, IExpressionEval>();
/**
* 所有RootAST节点的计算结果均以String格式返回
*/
public VarValue eval(RootAST root, IVarTable vartable, IClassTable classtable) {
// 判断变量表是否为null
if(vartable == null) {
logger.error("[Argument error],vartable is null.begin show");
root.show(0);
return null;
}
// Part1:无子节点的叶子部分节点的评估计算规则
// step0.判斷是不是中间节点
if (root instanceof MiddleAST) {
MiddleAST middle = (MiddleAST) root;
RootAST child = middle.getChildren().get(0);
// 跳过MiddleAST节点,递归调用
return eval(child, vartable, classtable);
}
// step1.如果root是常量节点,则返回常量
if (root instanceof AbstractConstDataElement) {
ConstEvalPlugin consteval = new ConstEvalPlugin();
return consteval.eval(root, vartable, classtable);
}
// step2.如果是root是一个变量节点,则返回变量结算结果
// 如果是由赋值运算符推动,则左面的变量作为参数使用,而不作为变量使用
if (root instanceof VarDataElement) {
VariableEvalPlugin vareval = new VariableEvalPlugin();
return vareval.eval(root, vartable, classtable);
}
// step3.如果root是一个new操作符,返回新创建的对象变量
if (root instanceof NewOperElement) {
NewOperEvalPlugin neweval = new NewOperEvalPlugin();
return neweval.eval(root, vartable, classtable);
}
// step4.如果root是一个变量定义操作符,则执行变量定义
if (root instanceof VarDefineOperElement) {
DefineOperEvalPlugin defineeval = new DefineOperEvalPlugin();
return defineeval.eval(root, vartable, classtable);
}
// step5.如果root是一个Atom,则调用Atom执行器执行
if (root instanceof AtomElement) {
AtomEvalPlugin atomeval = new AtomEvalPlugin();
return atomeval.eval(root, vartable, classtable);
}
// Part2. 一元运算符的支持部分,暂时只支持逻辑取否的运算符
// step6.逻辑取反的操作支持
if (root instanceof LogicNotOperElement) {
LogicNotPlugin logicnoteval = new LogicNotPlugin();
return logicnoteval.eval(root, vartable, classtable);
}
if (root instanceof DualOperDataOperElement) {
DualOperDataOperElement oper = (DualOperDataOperElement) root;
// 不同的运算符,调用不同的处理器
IExpressionEval eeval = this.getEvalPluginByOpercode(oper.getOpercode());
if(eeval == null) {
logger.error("Cannot find oper expressioneval:"+oper.getOpercode());
return null;
}
return eeval.eval(root, vartable, classtable);
}
// Part3.二元运算符的支持部分,这一部分也全部依据表达式重写
// step7 赋值运算符的支持
if (root instanceof VarSetOperElement) {
VarSetEvalPlugin varseteval = new VarSetEvalPlugin();
return varseteval.eval(root, vartable, classtable);
}
// Part4 对象调用的部分
// TODO:待补充这部分内容
// Part5 如果执行都这里,说明上面的逻辑都没有命中
// 这种情况下是属于执行错误
logger.error("---->执行出错了。");
root.show(0);
return null;
}
/**
* MEMO:根据运算符来获取对应的计算处理插件
* @param opercode
* @return
*/
private IExpressionEval getEvalPluginByOpercode(String opercode) {
initEvalMap();
return evalmap.get(opercode);
}
private static void initEvalMap() {
if (evalmap.size() == 0) {
//先加入算术运算符
evalmap.put("+", new MathAddOperEvalPlugin());
evalmap.put("-", new MathDeAddOperEvalPlugin());
evalmap.put("*", new MathMultiOperEvalPlugin());
evalmap.put("/", new MathDevideOperEvalPlugin());
//再加入逻辑运算符
evalmap.put("&&", new LogicAndOperEvalPlugin());
evalmap.put("||", new LogicOrOperEvalPlugin());
evalmap.put(">", new LogicGreaterOperEvalPlugin());
evalmap.put(">=", new LogicGreaterEqualOperEvalPlugin());
evalmap.put("<", new LogicLitterOperEvalPlugin());
evalmap.put("<=", new LogicLitterEqualOperEvalPlugin());
evalmap.put("==", new LogicEqualsOperEvalPlugin());
evalmap.put("!=", new LogicNotEqualOperEvalPlugin());
}
}
}
4应用场景讨论及可行性分析
本节针对第一章节提供的各种应用场景进行详细的实现设计讨论。
4.1浏览器/移动端的Java脚本(Java Runtime)
SmallJavaV2.0目前是使用Java作为编程语言,在JVM上使用的,它把Java源代码通过字符串分解的方式逐次解析成JavaFile,Class,Method,Block,Expression五个层次,最后分解成最基本的Java 表达式因子,然后再反向进行评估计算,最终达到动态执行Java类里面Java脚本的目的。那么我们可以将整个的主要过程分解出来,逐一评估迁移到其他平台时候的技术可行性。
SmallJava核心功能点
浏览器迁移评估
移动端Andorid迁移评估
JavaFile AST分析
主要算法为字符串切分
支持
支持
JavaClass AST分析
主要算法为字符串切分
支持
支持
Method AST分析
主要算法为字符串切分
支持
支持
Block AST分析
主要算法为字符串切分
支持
支持
Expression AST分析
主要算法为字符串切分
支持
支持
层次型变量表
主要算法为HashMap
支持
支持
Import 语句支持
不支持,JS中不可以直接引入class格式的java类
支持,Andorid支持引入Java class
JavaClass 类加载功能
初始化类实例的变量表
支持
支持
JavaClass 类实例功能
生成一个新实例的变量表
支持
支持
JavaMethod 实例化
生成一个方法实例对应变量表
支持
支持
Java Method调用
通过调用Block执行器来实现
支持
支持
Block 实例化及执行功能
通过调用Expression执行功能来实现
支持
支持
Expression实例化及执行功能。通过调用各基本Expression的执行功能来实现
支持
支持
Java类调用,通过Java的反射机制来支持
不支持,JS无法直接加载Java的class文件,也无法通过反射方式来支持。
支持,Andorid支持class文件加载,也支持Java反射技术
由上面的分析可以看出,由于目前JS设计无法直接加载Java的class文件,因此无法通过反射技术来实现现有Java类的方法调用,但反过来可以通过JS的方法调用技术来调用JavaScript编写的各个类和现有组件功能,不过那样一来,就是纯粹JavaScript的开发,而不再是Java编程了。
关于这种情况,可以采用前后端分离以后Ajax封装的方法来进行抽象和改造,不过这样一来开发的难度就加大了很多,还需要更进一步讨论分析。
相对于浏览器支持来说,Andorid平台由于源于Java技术,单从原理上来说,其支持程度要比浏览器更方便一些,这一点待以后再找时间设计验证。
4.3 JVM内部沙盒(Sandbox)设计
从上面SmallJavaV2.0的设计概念来看,SmallJava实际上是设计实现了一个基于Java源代码级别的Java运行环境,这个运行环境可以看成分为两大部分,一部分是本身使用SmallJava的源代码实现的功能,这些功能是不依赖于其他第三方Jar包实现的功能;另一部分是SmallJava通过import语句调用Java虚拟机本身,实现对JDK工具类和其他第三方Jar包内功能类的调用。
在Java语言中,通过Import语句引入第三方类库和调用类库都是非常方便的,但这种方便性反过来也很容易造成系统的安全漏洞和API的错误使用,造成负面效果,为了系统的安全性和用户管理的考虑,我们需要设计一个应用沙盒,通过对沙盒的权限范围进行相关的配置,避免用户的越权访问。
如果直接在JVM层次上试图实现对Import语句的控制,对Java类调用API的控制,由于Java源代码在运行时已经编译成标准的中间码,因此就必须通过动态修改Java字节码的方式,例如通过AOP等方式来修改JAVA字节码,以达到API层次使用限制的目的;这种方法具有一定的复杂性,开发难度较大。而且从系统设计的角度来说,直接修改字节码等于在一个黑盒状态下修改了程序本身,使得应用程序的源代码和最终执行的字节码不能一一对应,造成应用系统后期维护的困难。
相对直接修改字节码的技术方案来说,可以通过源代码的方式来加载Java类源文件,而在使用Import命令间接加载第三方类库时,这一加载功能是通过SmallJava的类加载功能插件来实现的,因此可以很方便的在类加载时进行功能扩展,包括对用户信息和授权机制进行集成判断,从而直接在import层次就直接拒绝未授权使用的类,这样就彻底杜绝了越权使用第三方API的可能性。这种权限控制的粒度,既可以控制在import语句的层次,也可以更加细化到类的实例化API调用的具体方法上,这样设计出来的应用沙盒就具有了很高的安全性和灵活性。
Import 语句执行器
类实例化
实例方法调用
类加载权限判断
配置信息
API调用权限判断
配置信息
4.4 JVM上动态代码解释执行
利用SmallJava,可以直接将一段Java代码字符串作为输入,在后台创建出一个临时的Java运行环境出来,直接执行这段代码。
在动态执行这段代码时,可以直接将这段代码对应的AST结构也输出给使用者,这样就可以直接在线查看代码究竟是如何被SmallJava解释器解释的,从而判断这个解释结果是否符合最初的用户设计的预期。
在SmallJavaV1.0版本中,已经支持在JVM上代码的动态解释执行,在SmallJavaV2.0中,这一功能仍然保留,但实际上需要通过包装成一个临时的匿名类的方法来进行调用,这样设计的目的是为了保持Java类文件加载的逻辑一致性,但这种差异对于调用者来说是透明的。
4.5 Java运行环境单步及调试(设计开发中)
由于SmallJava本身已经是一个完整的分析和运行环境,因此可以在代码执行时,通过增加断点设置、单步执行等新型执行接口来实现Java运行环境下的单步执行以及在线调试功能,而同时无须对JVM进行其他的修改变动。
这一功能对于相对复杂的生产环境下的代码功能调试时非常有用的。
这一功能和调用接口将在后续版本进行支持,目前处于设计计划阶段。
4.6 中文编程可行性分析
直接使用中文关键词来进行代码编程曾经是一个非常困难的事情,这主要是因为很多的工具本身不支持中文字符,因此在使用这些工具时可能会带来额外的BUG,出现问题也难以调试和解决。
但使用SmallJava进行编程语言的扩展时,使用中文关键词来进行编程就会变得容易很多,这是因为SmallJava在语法分析的阶段,没有依赖语法定义文件,也没有依赖第三方的语法分析工具,所有的语法分析工作完全是依赖于字符串的识别和解析来完成的。因此从下面五个层次来分析,就可以分析得出结论,使用SmallJava可以完整支持所有的中文关键词,原理上是完全可行的,具体实现需要根据实际应用情况进行定制开发,这主要是因为标准的Java语言中没有中文关键词,因此SmallJava一开始并没有这部分关键词的插件支持。
SmallJava核心功能
中文关键词、标识符支持分析
备注
变量表组件
支持
支持中文标识符
JavaFile分析
可以支持
目前标识符只支持英文字符,需要扩展中文标识符插件来支持
JavaClass分析
可以支持
目前标识符只支持英文字符,需要扩展中文标识符插件来支持
JavaMethod分析
可以支持
目前标识符只支持英文字符,需要扩展中文标识符插件来支持
Block分析
可以支持
目前Block支持if,dowhile,if,while这些流程关键词,可以根据需要增加中文的流程控制关键词来支持中文流程控制
Expression分析
可以支持
目前Expression支持标准的二元算术运算符和二元的逻辑运算符,可以根据需要设计中文算符插件来支持中文的计算符号
Expression执行
支持
在AST分析结束以后,AST分析结果中的中文和英文是作为同等的标识符来对待的,因此完全可以支持中文运算符的支持,中文的流程控制符的执行,不需要额外的开发工作
4.7分布式执行可行性分析
在SmallJavaV2.0设计中,Space和Time这两部分是完全分离的,其中的Space主要体现在变量表的设计和实现,变量表在提供给Time部分执行时,是通过接口定义的方式提供的,而不是通过具体实现的方式来提供。目前变量表默认是使用HashMap来作为最终的存储方案,变量表和AST执行部分运行在同一个JVM中。但变量表同样可以提供基于其他方式的实现,例如可以基于后台数据库作为存储,基于Redis内存作为存储,这种情况下代码运行时,时间要素和空间要素就是运行在不同JVM上,不同服务器之间的。
通过这种方式来实现分布式的应用,代码的执行完全可以一部分在一台机器完成,另一部分在另一台机器执行,因为在本地执行的代码实际上并没有访问本地的内存,因此也不需要考虑集群环境下运行时各机器之间内存数据的同步性。在这种模式下运行时,可以认为Space本身是集中式的,而Time元素是分布式运行的。
这种Space和Time的完全分离的设计,对于传统的Java开发来说,是相当困难的,因为Java代码在编译后运行时,默认两者是运行在同一个JVM的内存空间的,而采用SmallJava来进行改造,由于其Space和Time设计一开始就是分离的,因此改造成完全的分布式系统相对要简单的多。这种情况下,为了协调各节点的工作,可能需要额外设计新的调度工具来协调各节点之间的工作。
5参考文献
《Java编程艺术》
SmallJava的设计灵感,最初来自一本《JAVA编程艺术》的书,在这本书里面,作者演示了如何使用Java语言来编写一个Basic语言解释器的全过程,这个Basic解释器名为SmallBasic,受这本书的启发,我们才设计开发了SmallJava解释器。
《编译原理》
这本书讲述了编译原理的核心概念和基础算法。
《The Tao of Programming》
编程之道,这本小册子从哲学角度对程序的本质进行解读。