-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JerryScript 源码学习相关 #479
Comments
源码数据结构JerryScript context 全局环境/**
* JerryScript context
*
jerry-core\jcontext\jcontext.h
* The purpose of this header is storing
* all global variables for Jerry
*
* 这个堆的目的是存储jerry的所有全局变量
*/
struct jerry_context_t
{
/* The value of external context members must be preserved across initializations and cleanups. */
// 必须在初始化和清理之间的的时候保留外部上下文成员的值。
#ifdef JERRY_ENABLE_EXTERNAL_CONTEXT
#ifndef JERRY_SYSTEM_ALLOCATOR
jmem_heap_t *heap_p; /**< point to the heap aligned to JMEM_ALIGNMENT. 指向与JMEM_ALIGNMENT对齐的堆。 */
uint32_t heap_size; /**< size of the heap */
#endif /* !JERRY_SYSTEM_ALLOCATOR */
#endif /* JERRY_ENABLE_EXTERNAL_CONTEXT */
/* Update JERRY_CONTEXT_FIRST_MEMBER if the first non-external member changes */
// 如果第一个非外部成员更改,请更新JERRY_CONTEXT_FIRST_MEMBER
ecma_object_t *ecma_builtin_objects[ECMA_BUILTIN_ID__COUNT]; /**< pointer to instances of built-in objects 指向内置对象实例的指针 */
#if ENABLED (JERRY_BUILTIN_REGEXP)
const re_compiled_code_t *re_cache[RE_CACHE_SIZE]; /**< regex cache 正则缓存 */
#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
ecma_object_t *ecma_gc_objects_p; /**< List of currently alive objects. 当前活动的对象列表。 */
jmem_heap_free_t *jmem_heap_list_skip_p; /**< This is used to speed up deallocation. 这用于加速释放堆缓存。 */
jmem_pools_chunk_t *jmem_free_8_byte_chunk_p; /**< list of free eight byte pool chunks 空余的八个字节的池块列表 */
#ifdef JERRY_CPOINTER_32_BIT
jmem_pools_chunk_t *jmem_free_16_byte_chunk_p; /**< list of free sixteen byte pool chunks 空余的16个字节的池块列表 */
#endif /* JERRY_CPOINTER_32_BIT */
jmem_free_unused_memory_callback_t jmem_free_unused_memory_callback; /**< Callback for freeing up memory. 释放内存的回调。 */
const lit_utf8_byte_t * const *lit_magic_string_ex_array; /**< array of external magic strings 外部魔术字符串数组 */
const lit_utf8_size_t *lit_magic_string_ex_sizes; /**< external magic string lengths 外部魔术弦长 */
ecma_lit_storage_item_t *string_list_first_p; /**< first item of the literal string list 常量字符串列表的第一项 */
#if ENABLED (JERRY_ES2015_BUILTIN_SYMBOL)
ecma_lit_storage_item_t *symbol_list_first_p; /**< first item of the global symbol list 全局符号列表的第一项 */
#endif /* ENABLED (JERRY_ES2015_BUILTIN_SYMBOL) */
ecma_lit_storage_item_t *number_list_first_p; /**< first item of the literal number list 常量数字列表的第一项 */
ecma_object_t *ecma_global_lex_env_p; /**< global lexical environment 全局ecma常量环境 */
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
ecma_module_t *ecma_modules_p; /**< list of referenced modules 引用模块的列表 */
ecma_module_context_t *module_top_context_p; /**< top (current) module parser context 顶级(当前)模块解析器上下文 */
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context 顶级(当前)解释器上下文 */
jerry_context_data_header_t *context_data_p; /**< linked list of user-provided context-specific pointers 用户提供的特定上下文的指针列表 */
size_t ecma_gc_objects_number; /**< number of currently allocated objects 当前分配对象的数量 */
size_t ecma_gc_new_objects; /**< number of newly allocated objects since last GC session 自上次GC会话以来新分配的对象数 */
size_t jmem_heap_allocated_size; /**< size of allocated regions 分配区域的大小 */
size_t jmem_heap_limit; /**< current limit of heap usage, that is upon being reached,
* causes call of "try give memory back" callbacks 当前堆使用的限制,快达到最大限制时,会导致调用“try give memory back”回调 */
ecma_value_t error_value; /**< currently thrown error value 目前抛出的错误值 */
uint32_t lit_magic_string_ex_count; /**< external magic strings count 外部魔术字符数 */
uint32_t jerry_init_flags; /**< run-time configuration flags 运行时配置标志 */
uint32_t status_flags; /**< run-time flags (the top 8 bits are used for passing class parsing options) 运行时标志(前8位用于传递类解析选项) */
#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE
uint8_t ecma_prop_hashmap_alloc_state; /**< property hashmap allocation state: 0-4,
* if !0 property hashmap allocation is disabled 属性hashmap分配状态:0-4,如果!0属性hashmap分配被禁用 */
#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */
#if ENABLED (JERRY_BUILTIN_REGEXP)
uint8_t re_cache_idx; /**< evicted item index when regex cache is full (round-robin) 正则缓存已满时推出的项的索引(循环法) */
#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
#if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
ecma_job_queueitem_t *job_queue_head_p; /**< points to the head item of the jobqueue 指向工作队列的头部项 */
ecma_job_queueitem_t *job_queue_tail_p; /**< points to the tail item of the jobqueue 指向工作队列的尾部项 */
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
#ifdef JERRY_VM_EXEC_STOP
uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */
uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */
void *vm_exec_stop_user_p; /**< user pointer for vm_exec_stop_cb */
ecma_vm_exec_stop_callback_t vm_exec_stop_cb; /**< user function which returns whether the
* ECMAScript execution should be stopped */
#endif /* JERRY_VM_EXEC_STOP */
#ifdef VM_RECURSION_LIMIT
uint32_t vm_recursion_counter; /**< VM recursion counter */
#endif /* VM_RECURSION_LIMIT */
#ifdef JERRY_DEBUGGER
uint8_t debugger_send_buffer[JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE]; /**< buffer for sending messages */
uint8_t debugger_receive_buffer[JERRY_DEBUGGER_TRANSPORT_MAX_BUFFER_SIZE]; /**< buffer for receiving messages */
jerry_debugger_transport_header_t *debugger_transport_header_p; /**< head of transport protocol chain */
uint8_t *debugger_send_buffer_payload_p; /**< start where the outgoing message can be written */
vm_frame_ctx_t *debugger_stop_context; /**< stop only if the current context is equal to this context */
uint8_t *debugger_exception_byte_code_p; /**< Location of the currently executed byte code if an
* error occours while the vm_loop is suspended */
jmem_cpointer_t debugger_byte_code_free_head; /**< head of byte code free linked list */
jmem_cpointer_t debugger_byte_code_free_tail; /**< tail of byte code free linked list */
uint32_t debugger_flags; /**< debugger flags */
uint16_t debugger_received_length; /**< length of currently received bytes */
uint8_t debugger_message_delay; /**< call receive message when reaches zero */
uint8_t debugger_max_send_size; /**< maximum amount of data that can be sent */
uint8_t debugger_max_receive_size; /**< maximum amount of data that can be received */
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
ecma_value_t resource_name; /**< resource name (usually a file name) */
#endif /* JERRY_ENABLE_LINE_INFO */
#ifdef JMEM_STATS
jmem_heap_stats_t jmem_heap_stats; /**< heap's memory usage statistics */
#endif /* JMEM_STATS */
/* This must be at the end of the context for performance reasons */
#ifndef CONFIG_ECMA_LCACHE_DISABLE
/** hash table for caching the last access of properties 哈希表,用于缓存属性的最后一次访问 */
ecma_lcache_hash_entry_t lcache[ECMA_LCACHE_HASH_ROWS_COUNT][ECMA_LCACHE_HASH_ROW_LENGTH];
#endif /* !CONFIG_ECMA_LCACHE_DISABLE */
}; context_p/**
* Those members of a context which needs
* to be saved when a sub-function is parsed.
* jerry-core/parser/js/js-parser-internal.h
* 解析子函数时需要保存的上下文成员。
*/
typedef struct parser_saved_context_t
{
/* Parser members. parser成员 */
uint32_t status_flags; /**< parsing options parser 选项 */
uint16_t stack_depth; /**< current stack depth 当前栈深度 */
uint16_t stack_limit; /**< maximum stack depth 最大栈深度 */
struct parser_saved_context_t *prev_context_p; /**< last saved context 最新保存的context */
parser_stack_iterator_t last_statement; /**< last statement position 最新声明的位置 */
/* Literal types */
uint16_t argument_count; /**< number of function arguments 函数参数的数量 */
uint16_t register_count; /**< number of registers 寄存器的数量 */
uint16_t literal_count; /**< number of literals 常量的数量 */
/* Memory storage members. */
parser_mem_data_t byte_code; /**< byte code buffer 字节码内存 */
uint32_t byte_code_size; /**< byte code size for branches branch字节码大小 */
parser_mem_data_t literal_pool_data; /**< literal list 常量列表 */
#ifndef JERRY_NDEBUG
uint16_t context_stack_depth; /**< current context stack depth 当前context的stack的深度 */
#endif /* !JERRY_NDEBUG */
} parser_saved_context_t;
/**
* Shared parser context.
* 共享的parser上下文,全局对象
*/
typedef struct
{
PARSER_TRY_CONTEXT (try_buffer); /**< try_buffer */
parser_error_t error; /**< error code */
void *allocated_buffer_p; /**< dinamically allocated buffer 动态分配缓冲区
* which needs to be freed on error 需要在出错时释放 */
uint32_t allocated_buffer_size; /**< size of the dinamically allocated buffer 动态分配缓冲区的大小 */
/* Parser members. parser阶段的成员 */
uint32_t status_flags; /**< status flags 状态标记 */
uint16_t stack_depth; /**< current stack depth 当前栈的深度 */
uint16_t stack_limit; /**< maximum stack depth 最大栈深度 */
parser_saved_context_t *last_context_p; /**< last saved context 上一次保存的context指针 */
parser_stack_iterator_t last_statement; /**< last statement position 上一次发表的位置 */
#if ENABLED (JERRY_ES2015_MODULE_SYSTEM)
ecma_module_node_t *module_current_node_p; /**< import / export node that is being processed 正在处理的import 或 export 节点 */
lexer_literal_t *module_identifier_lit_p; /**< the literal for the identifier of the current element 当前元素标识符的文字 */
#endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
/* Lexer members. lexer的成员 */
lexer_token_t token; /**< current token 当前token */
lexer_lit_object_t lit_object; /**< current literal object 当前词对象 */
const uint8_t *source_p; /**< next source byte 下一个代码的数据 */
const uint8_t *source_end_p; /**< last source byte 上一个代码的数据 */
parser_line_counter_t line; /**< current line 当前lexe的行 */
parser_line_counter_t column; /**< current column 当前lexe的列 */
/* Compact byte code members. 压缩字节码的成员 */
cbc_argument_t last_cbc; /**< argument of the last cbc 上一个字节码的参数 */
uint16_t last_cbc_opcode; /**< opcode of the last cbc 上一个字节码的操作码 */
/* Literal types 词的类型 */
uint16_t argument_count; /**< number of function arguments 函数参数的数量 */
uint16_t register_count; /**< number of registers 寄存器的数量 */
uint16_t literal_count; /**< number of literals 文字的数量 */
/* Memory storage members. 内存存储成员。 */
parser_mem_data_t byte_code; /**< byte code buffer 字节码内存 */
uint32_t byte_code_size; /**< current byte code size for branches 当前branches字节代码大小 */
parser_list_t literal_pool; /**< literal list 常量列表*/
parser_mem_data_t stack; /**< storage space 存储空间 */
parser_mem_page_t *free_page_p; /**< space for fast allocation 可快速分配的空间 */
uint8_t stack_top_uint8; /**< top byte stored on the stack 存储在堆栈中的顶部字节 */
#ifndef JERRY_NDEBUG
/* Variables for debugging / logging. */
uint16_t context_stack_depth; /**< current context stack depth 当前context栈的深度 */
#endif /* !JERRY_NDEBUG */
#ifdef PARSER_DUMP_BYTE_CODE
int is_show_opcodes; /**< show opcodes 展示操作码 */
uint32_t total_byte_code_size; /**< total byte code size 总共代码大小 */
#endif /* PARSER_DUMP_BYTE_CODE */
#ifdef JERRY_DEBUGGER
parser_breakpoint_info_t breakpoint_info[PARSER_MAX_BREAKPOINT_INFO_COUNT]; /**< breakpoint info list */
uint16_t breakpoint_info_count; /**< current breakpoint index */
parser_line_counter_t last_breakpoint_line; /**< last line where breakpoint has been inserted */
#endif /* JERRY_DEBUGGER */
#ifdef JERRY_ENABLE_LINE_INFO
parser_line_counter_t last_line_info_line; /**< last line where line info has been inserted */
#endif /* JERRY_ENABLE_LINE_INFO */
} parser_context_t;
/**
* Context of interpreter, related to a JS stack frame
* 解析器的全局上下文,根JS栈帧相关
栈帧 jerry-core/vm/vm-defines.h
*/
typedef struct vm_frame_ctx_t
{
const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data 当前执行的字节码数据 */
uint8_t *byte_code_p; /**< current byte code pointer 当前字节码指针 */
uint8_t *byte_code_start_p; /**< byte code start pointer 字节码凯撒指针 uint32_t */
ecma_value_t *registers_p; /**< register start pointer 注册开始指针 uint32_t 4字节 */
ecma_value_t *stack_top_p; /**< stack top pointer 栈顶指针 uint32_t 4字节 */
ecma_value_t *literal_start_p; /**< literal list start pointer 字面量列表开始指针*/
ecma_object_t *lex_env_p; /**< current lexical environment 当前lexer环境 */
struct vm_frame_ctx_t *prev_context_p; /**< previous context 之前一个context */
ecma_value_t this_binding; /**< this binding */
ecma_value_t block_result; /**< block result 块结果 */
#ifdef JERRY_ENABLE_LINE_INFO
ecma_value_t resource_name; /**< current resource name (usually a file name) 当前资源的名称,通常是文件名 */
uint32_t current_line; /**< currently executed line 当前执行的行 */
#endif /* JERRY_ENABLE_LINE_INFO */
uint16_t context_depth; /**< current context depth 当前context的深度 */
uint8_t is_eval_code; /**< eval mode flag eval摸索标识 */
uint8_t call_operation; /**< perform a call or construct operation 执行调用或构造操作 */
} vm_frame_ctx_t; 每个token的数据结构/**
* Literal data.
* 常量数据结构,单个token的数据结构
*/
typedef struct
{
union
{
ecma_value_t value; /**< literal value (not processed by the parser) 常量值(未由解析器处理) */
const uint8_t *char_p; /**< character value 字母值 */
ecma_compiled_code_t *bytecode_p; /**< compiled function or regexp pointer 编译函数或正则表达式的指针 */
uint32_t source_data; /**< encoded source literal 编码的源常量 */
} u;
#ifdef PARSER_DUMP_BYTE_CODE
struct
#else /* !PARSER_DUMP_BYTE_CODE */
union
#endif /* PARSER_DUMP_BYTE_CODE */
{
prop_length_t length; /**< length of ident / string literal 关键字的长度 */
uint16_t index; /**< real index during post processing 在提交过程中真实的index */
} prop;
uint8_t type; /**< type of the literal 常量的类型 */
uint8_t status_flags; /**< status flags 状态标志 */
} lexer_literal_t; var aa = {
token: {
type: 0, // 这是一个指针,是token的类型,lexer_token_type_t其中的一员
literal_is_reserved: 0, // 指针
extra_value: 255, // 指针,一般是‘\377’
flags: 0, // 指针
line: 1, // 数值,代码的第几行
column: 9, // number, 代码在第几个,相当于列的位置
},
// `source_p[0]`就是`lexer_next_token`函数中分割出来要判断token type的代码片段开始位,是一个指针,具体词语可以根据line,column和length定位
source_p,
// 这个好像是lexe后的结果,是一个iterator
// page_p就是stack的一员
stack: {
// 这个好
first_p,
last_p,
lastpositon
},
byte_code: {},
lit_object: {},
free_page_p: {}
} jerry_global_heap/**
* Free region node
* 自由区域节点
jerry-core\jmem\jmem.h
*/
typedef struct
{
uint32_t next_offset; /**< Offset of next region in list 列表中下一个区域的偏移量 */
uint32_t size; /**< Size of region 区域大小 */
} jmem_heap_free_t;
// jerry-core\jcontext\jcontext.h
struct jmem_heap_t
{
jmem_heap_free_t first; /**< first node in free region list 自由区域列表中的第一个节点 */
uint8_t area[JMEM_HEAP_AREA_SIZE]; /**< heap area 头部区域 */
}; parser
lexerparser_parse_statements
lexer_next_token这个函数的作用:
lexer_skip_spaces筛选截取到的代码段属于哪个token type,然后通过修改type,flags,line和column跳过。单纯就是跳过空格token而已 lexer_parse_identifier这个函数是关键字和参数的寻找,context_p下有三个字段跟关键字定位相关,分别是:line(行),column(列),length(长度)。原代码语句存储在source_p里面 lexer_parse_string这个也很关键,循环确定变量名称 字节码
ecma_value_t说明, /**
* Description of an ecma value
* 描述一个ecma值
*
* Bit-field structure: type (3) | value (29)
* 字段位结构体:类型占3位,值占29
比如72是64+8 = 1001000,所以类型是000
*/
typedef uint32_t ecma_value_t; 类型有: /**
* Type of ecma value
* ecma值的类型
*/
typedef enum
{
ECMA_TYPE_DIRECT = 0, /**< directly encoded value, a 28 bit signed integer or a simple value 直接编码数值,是一个28位有符号整数或在一个简单的值 */
ECMA_TYPE_STRING = 1, /**< pointer to description of a string 描述一个字符串的指针 */
ECMA_TYPE_FLOAT = 2, /**< pointer to a 64 or 32 bit floating point number 32或64位浮点小数的指针 */
ECMA_TYPE_OBJECT = 3, /**< pointer to description of an object 对象的指针 */
ECMA_TYPE_SYMBOL = 4, /**< pointer to description of a symbol symbol的指针 */
ECMA_TYPE_DIRECT_STRING = 5, /**< directly encoded string values 直接编码的字符串值 */
ECMA_TYPE_ERROR = 7, /**< pointer to description of an error reference (only supported by C API) 指向错误引用描述的指针(仅由C API支持) */
ECMA_TYPE_POINTER = ECMA_TYPE_ERROR, /**< a generic aligned pointer 通用对齐指针 */
ECMA_TYPE_SNAPSHOT_OFFSET = ECMA_TYPE_ERROR, /**< offset to a snapshot number/string 偏移到快照数字/字符串 */
ECMA_TYPE___MAX = ECMA_TYPE_ERROR /** highest value for ecma types ecma类型的最高价值 */
} ecma_type_t; parser阶段的内存结构/**
* All data allocated by the parser is
* stored in parser_data_pages in the memory.
*
* 解析器分配的所有数据堆都是存储在内存中的parser_data_pages中。
*/
typedef struct parser_mem_page_t
{
struct parser_mem_page_t *next_p; /**< next page 下一页 */
uint8_t bytes[1]; /**< memory bytes 内存字节,就是存储的数据 */
} parser_mem_page_t;
/**
* Structure for managing parser memory.
*
* 用于管理解析器内存的结构。其实就是一个连表
*/
typedef struct
{
parser_mem_page_t *first_p; /**< first allocated page 第一个分配的页 */
parser_mem_page_t *last_p; /**< last allocated page 最后分配的页 */
uint32_t last_position; /**< position of the last allocated byte 最后分配字节的位置 */
} parser_mem_data_t;
/**
* Parser memory list.
* 解析器内存链表
*/
typedef struct
{
parser_mem_data_t data; /**< storage space 存储空间*/
uint32_t page_size; /**< size of each page 每一页大小 */
uint32_t item_size; /**< size of each item 每一项大小 */
uint32_t item_count; /**< number of items on each page 每一页有多少项 */
} parser_list_t;
/**
* Iterator for parser memory list.
* parser内存列表
*/
typedef struct
{
parser_list_t *list_p; /**< parser list parser 列表 可以迭代存储书记*/
parser_mem_page_t *current_p; /**< currently processed page 当前处理的页,可以迭代存储 */
size_t current_position; /**< current position on the page 这页当前的位置 */
} parser_list_iterator_t;
/**
* Parser memory stack.
*/
typedef struct
{
parser_mem_data_t data; /**< storage space */
parser_mem_page_t *free_page_p; /**< space for fast allocation */
} parser_stack_t; 注意主要的类型目录有两个,在:
#define LEXER_BINARY_OP_TOKEN_TO_OPCODE(token_type) \
((cbc_opcode_t) ((((token_type) - LEXER_BIT_OR) * 3) + CBC_BIT_OR)) 问题
|
进度
#include "jerryscript.h"
int
main (void)
{
const jerry_char_t script[] = "var str = 'Hello, World!';";
bool ret_value = jerry_run_simple (script, sizeof (script) - 1, JERRY_INIT_EMPTY);
return (ret_value ? 0 : 1);
}
|
parser上图显示了JerryScript的主要组件之间的交互:Parser和虚拟机(VM)。 解析器将输入ECMAScript应用程序转换为具有指定格式的字节代码(有关详细信息,请参阅字节码和解析器页面)。 准备好的字节码由执行解释的虚拟机执行(有关详细信息,请参阅虚拟机和ECMA页面)。 Parser解析器实现为递归下降解析器。 解析器将JavaScript源代码直接转换为字节码,而无需构建抽象语法树。 解析器依赖于以下子组件。 lexer词法分析器将输入字符串(ECMAScript程序)拆分为token序列。 它不仅可以向前扫描输入字符串,还可以移动到任意位置。 由./jerry-core/parser/js/js-lexer.h中的结构lexer_token_t描述的token结构。 scanner扫描程序(./jerry-core/parser/js/js-parser-scanner.c)预扫描输入字符串以查找某些token。 例如,扫描程序确定关键字是否定义了for或for-for循环。 在while循环中读取tokens是不够的,因为斜杠(/)可以表示正则表达式的开始或者是除法运算符。 Expression Parser表达式解析器负责解析JavaScript表达式。 它在./jerry-core/parser/js/js-parser-expr.c中实现。 Statement ParserJavaScript语句由此组件解析。 它使用Expression解析器来解析组成表达式。 Statement解析器的实现位于 函数 紧凑字节码(
|
byte-code | description |
---|---|
CBC_PUSH_LITERAL | Pushes the value of the given literal argument. |
CBC_PUSH_TWO_LITERALS | Pushes the value of the given two literal arguments. |
CBC_PUSH_UNDEFINED | Pushes an undefined value. |
CBC_PUSH_TRUE | Pushes a logical true. |
CBC_PUSH_PROP_LITERAL | Pushes a property whose base object is popped from the stack, and the property name is passed as a literal argument. |
call字节码,调用字节码
byte-code | description |
---|---|
CBC_CALL0 | Calls a function without arguments. The return value won't be pushed onto the stack. |
CBC_CALL1 | Calls a function with one argument. The return value won't be pushed onto the stack. |
CBC_CALL | Calls a function with n arguments. n is passed as a byte argument. The return value won't be pushed onto the stack. |
CBC_CALL0_PUSH_RESULT | Calls a function without arguments. The return value will be pushed onto the stack. |
CBC_CALL1_PUSH_RESULT | Calls a function with one argument. The return value will be pushed onto the stack. |
CBC_CALL2_PROP | Calls a property function with two arguments. The base object, the property name, and the two arguments are on the stack. |
算术运算,逻辑运算,位运算,赋值运算,字节码
byte-code | description |
---|---|
CBC_LOGICAL_NOT | Negates the logical value that popped from the stack. The result is pushed onto the stack. |
CBC_LOGICAL_NOT_LITERAL | Negates the logical value that given in literal argument. The result is pushed onto the stack. |
CBC_ADD | Adds two values that are popped from the stack. The result is pushed onto the stack. |
CBC_ADD_RIGHT_LITERAL | Adds two values. The left one popped from the stack, the right one is given as literal argument. |
CBC_ADD_TWO_LITERALS | Adds two values. Both are given as literal arguments. |
CBC_ASSIGN | Assigns a value to a property. It has three arguments: base object, property name, value to assign. |
CBC_ASSIGN_PUSH_RESULT | Assigns a value to a property. It has three arguments: base object, property name, value to assign. The result will be pushed onto the stack. |
Branch,分支字节码
branch就是跳转,分支字节代码用于在字节代码中执行条件和无条件跳转。 这些指令的参数是1-3字节长的相对偏移量。 它占用的位数是操作码(opcode)位数的一部分,因此每个带有branch参数的字节码都有三种形式。因为偏移是无符号值,方向(前向,后向)也由操作码定义。 因此,某些分支指令有六种形式。 可以在下表中找到一些示例。
byte-code | description |
---|---|
CBC_JUMP_FORWARD | Jumps forward by the 1 byte long relative offset argument. |
CBC_JUMP_FORWARD_2 | Jumps forward by the 2 byte long relative offset argument. |
CBC_JUMP_FORWARD_3 | Jumps forward by the 3 byte long relative offset argument. |
CBC_JUMP_BACKWARD | Jumps backward by the 1 byte long relative offset argument. |
CBC_JUMP_BACKWARD_2 | Jumps backward by the 2 byte long relative offset argument. |
CBC_JUMP_BACKWARD_3 | Jumps backward by the 3 byte long relative offset argument. |
CBC_BRANCH_IF_TRUE_FORWARD | Jumps if the value on the top of the stack is true by the 1 byte long relative offset argument. |
Snapshot 快照
编译后的字节码可以保存到快照中,快照可以重新加载和执行。 直接执行快照可以节省在parser源码中的内存消耗和性能。 快照也可以从ROM执行,在这种情况下,也可以保存将其加载到内存中的开销。(The snapshot can also be executed from ROM, in which case the overhead of loading it into the memory can also be saved.)
虚拟机
虚拟机是一个逐条执行字节码指令的解释器。 启动解释的函数是./jerry-core/vm/vm.c
中的vm_run
。 vm_loop
是虚拟机的主循环,具有非递归(不是重复调用自己)的特性。调用函数时,它不会递归地调用自身而是返回调用结果,这样做的好处是它不用负担整个堆栈递归的执行。
ECMA
ECMA组件组要负责引擎的:
- 数据表示, Data representation
- 运行时表示, Runtime representation
- 垃圾回收(GC)
数据表示,Data Representation
数据展示的主要结构体是ECMA_value
,最后的两位用于编码数值的类型,比如:simple,number,string,object。结构是:
| value | err | type |
- value, 29bit
- err,1bit
- type,2bit
在数字,字符串和对象的情况下,value可以是一个指针。simple是预定义的常量,可以是:undefined,null,true,false,empty(没有初始化的值)
压缩指针,Compressed Pointers
这些指针是8字节对齐的16位长指针,可以处理512 Kb的内存,这也是JerryScript堆的最大大小。 为了支持更多的内存,压缩指针的大小可以扩展到32位,通过将“--cpointer_32_bit on”传递给构建系统来覆盖32位系统的整个地址空间。 这些“未压缩指针”消耗的内存将增加大约20%。
数字,number
根据标准IEEE 754,有两种可能的数字表示形式:默认值为8字节(双精度),但引擎通过设置CONFIG_ECMA_NUMBER_TYPE也支持4字节(单精度)表示。
多个对数字的单分配(allocated)的引用是不支持的。 每个引用都拥有自己的数字副本。
字符串
JerryScript中的字符串不仅仅是字符序列,而且还可以包含数字和所谓的魔法ID。 对于常见字符序列(在./jerry-core/lit/lit-magic-strings.ini
中定义),在只读存储器中有一个表,其中包含魔术ID和字符序列对。 如果此表中已存在字符串,则会存储其字符串的魔术ID,而不是字符序列本身。 使用数字可加快属性访问速度。 这些技术节省了内存。
对象/词汇环境 Object / Lexical Environment
对象可以是传统数据对象或词汇环境对象。 与其他数据类型不同,对象可以具有对其他数据类型的引用(称为属性)。 由于循环引用,引用计数并不总是足以确定死对象。 因此,生成一条根据所有现有对象形成的链表,用于在垃圾回收期间查找未引用的对象。 每个对象的gc-next指针显示链表中的下一个已分配对象。
词法环境在JerryScript中实现为对象,因为词法环境包含像对象一样的键值对(称为绑定)。 这简化了实现并减少了代码大小。
对象/ Lexicat环境结构
- 对象表示为以下结构:
- 引用计数器 - 硬(非属性)引用的数量
- 垃圾收集器的下一个对象指针
- GC的访问标志
- 类型(功能对象,词汇环境等)
对象的属性 Properties of Objects
对象有包含其属性的链接(指针)列表。 该列表实际上包含属性对,以便节省下面情况需要的内存:属性是7位长,其类型字段是2位长,消耗9位,不适合1个字节但消耗2个字节。 因此,将两个属性(14位)与2位长类型字段放在一起适合2个字节。
属性Hashmap
如果属性对的数量达到限制(当前此限制被定义为16),则会在属性对列表的第一个位置插入一个hash map(称为Property Hashmap),以便使用它来查找属性,而不是通过遍历属性对来找到它。
属性hashmap包含2^n个元素,其中2^n是大于对象属性的数量。每个元素都可以具有树类型的值有:
- null,表示空元素
- deleted,表示已删除的属性,或别的
- 对现有属性的引用
此hashmap是必须返回的类型缓存,这意味着可以使用它找到该对象具有的每个属性。
内部属性
内部属性是特殊属性,它们虽然携带的meta-information
无法被JavaScript代码访问,但对引擎本身还是很重要的。下面列出了一些内部属性的示例:
- [[Class]] - 对象的类(类型)(ECMA定义)
- [[Code]] - 指向查找函数字节码的位置
- native code - 指向查找本机函数代码的位置
- [[PrimitiveValue]] for Boolean - 存储布尔对象的布尔值
- [[PrimitiveValue]] for Number - 存储Number对象的数值
LCache
LCache是一个根据对象和属性名称寻找属性的散列映射。 LCache的object-name-property
结构会被频繁调用,如下图所示。
当要访问属性时,会根据所需属性的名称生成一个哈希值,然后用该哈希值在LCache中要到要找的对象和属性
需要注意的是,如果在LCache中找不到需要的属性,并不意味着它不存在(即LCache是一个可能返回的缓存)。 如果在LCache中找不到该属性,则会在对象的属性列表中搜索该属性,如果在该对象的属性列表中找到了该属性,该属性就会被放入LCache中。
集合 Collections
集合是为了节省内存而做的类似于数组的数据结构。实际上,集合是一个链表,其元素不是单个元素,而是可以包含多个元素的数组。
错误处理 Exception Handling
为了实现一个基本满足使用的异常处理,JerryScript函数在返回值里面告诉外面错误或“异常”操作。返回值实际上是ECMA值(参见数据表示部分),如果发生错误操作,则设置错误位。
数值管理和所有权 Value Management and Ownership
引擎所存储的每个ECMA值都与虚拟的“所有权”相关联,该“所有权”定义了如何管理值:何时在不再需要时释放它以及如何将值传递给其他函数。
初始状态,值由其所有者(即拥有所有权)分配。所有者负责释放分配的值。当值作为参数传递给函数时,其所有权并不会被传递给函数。被调用的函数必须自己创建这个值的副本才行。但是,只要函数返回一个值,所有权就会发生传递,要想释放它只能靠调用者。
jerryscript字节码相关注意
/**
* Type of ecma value
*/
typedef enum
{
ECMA_TYPE_DIRECT = 0, /**< directly encoded value, a 28 bit signed integer or a simple value */
ECMA_TYPE_STRING = 1, /**< pointer to description of a string */
ECMA_TYPE_FLOAT = 2, /**< pointer to a 64 or 32 bit floating point number */
ECMA_TYPE_OBJECT = 3, /**< pointer to description of an object */
ECMA_TYPE_SYMBOL = 4, /**< pointer to description of a symbol */
ECMA_TYPE_DIRECT_STRING = 5, /**< directly encoded string values */
ECMA_TYPE_ERROR = 7, /**< pointer to description of an error reference (only supported by C API) */
ECMA_TYPE_POINTER = ECMA_TYPE_ERROR, /**< a generic aligned pointer */
ECMA_TYPE_SNAPSHOT_OFFSET = ECMA_TYPE_ERROR, /**< offset to a snapshot number/string */
ECMA_TYPE___MAX = ECMA_TYPE_ERROR /** highest value for ecma types */
} ecma_type_t;
/**
* Shift for value part in ecma_value_t
*/
#define ECMA_VALUE_SHIFT 3
/**
* Ecma simple value type
*/
#define ECMA_DIRECT_TYPE_SIMPLE_VALUE ((1u << ECMA_VALUE_SHIFT) | ECMA_TYPE_DIRECT)
/**
* Shift for directly encoded values in ecma_value_t
*/
#define ECMA_DIRECT_SHIFT 4
/**
* ECMA make simple value
*/
#define ECMA_MAKE_VALUE(value) \
((((ecma_value_t) (value)) << ECMA_DIRECT_SHIFT) | ECMA_DIRECT_TYPE_SIMPLE_VALUE)
/**
* Simple ecma values
*/
enum
{
/**
* Empty value is implementation defined value, used for representing:
* - empty (uninitialized) values
* - immutable binding values
* - special register or stack values for vm
* 空值是实现定义值,用于表示:
* - 空(未初始化)值
* - 不可变的绑定值
* - vm的特殊寄存器或堆栈值
*/
ECMA_VALUE_EMPTY = ECMA_MAKE_VALUE (0), /**< uninitialized value 8=1000 */
ECMA_VALUE_ERROR = ECMA_MAKE_VALUE (1), /**< an error is currently thrown 24=11000 */
ECMA_VALUE_FALSE = ECMA_MAKE_VALUE (2), /**< boolean false 40=101000 */
ECMA_VALUE_TRUE = ECMA_MAKE_VALUE (3), /**< boolean true 56=111000 */
ECMA_VALUE_UNDEFINED = ECMA_MAKE_VALUE (4), /**< undefined value 72=1001000 */
ECMA_VALUE_NULL = ECMA_MAKE_VALUE (5), /**< null value 88=1011000*/
ECMA_VALUE_ARRAY_HOLE = ECMA_MAKE_VALUE (6), /**< array hole, used for
* initialization of an array literal 104=1101000 */
ECMA_VALUE_NOT_FOUND = ECMA_MAKE_VALUE (7), /**< a special value returned by
* ecma_op_object_find 120=1111000 */
ECMA_VALUE_REGISTER_REF = ECMA_MAKE_VALUE (8), /**< register reference,
* a special "base" value for vm 136=10001000 */
ECMA_VALUE_IMPLICIT_CONSTRUCTOR = ECMA_MAKE_VALUE (9), /**< special value for bound class constructors 152=10011000 */
};
/**
byte-code.c
* Compact bytecode definition
* 紧凑的字节码定义
*/
#define CBC_OPCODE(arg1, arg2, arg3, arg4) \
((arg2) | (((arg3) + CBC_STACK_ADJUST_BASE) << CBC_STACK_ADJUST_SHIFT)), // vm.c
/** Compact bytecode define */
#define CBC_OPCODE(arg1, arg2, arg3, arg4) arg4, /**
* Compact byte code (CBC) is a byte code representation
* of EcmaScript which is designed for low memory
* environments. Most opcodes are only one or sometimes
* two byte long so the CBC provides a small binary size.
*
* The execution engine of CBC is a stack machine, where
* the maximum stack size is known in advance for each
* function.
紧凑字节代码(CBC)是表示EcmaScript的字节码,这样设计的目的是适应低内存环境。
大多数操作码只有一个或有时两个字节长,所以CBC可以提供了一个小的二进制打包大小。
CBC的执行引擎是一个堆栈机器,在那里每个函数都预先知道最大堆栈大小
*/
/**
* Byte code flags. Only the lower 5 bit can be used
* since the stack change is encoded in the upper
* three bits for each instruction between -4 and 3
* (except for call / construct opcodes).
字节代码标志。 只能使用低5位,因为堆栈更改是在每个指令上层的三位,在-4和3之间(调用/构造操作码除外)。
*/
#define CBC_STACK_ADJUST_BASE 4
#define CBC_STACK_ADJUST_SHIFT 5
#define CBC_STACK_ADJUST_VALUE(value) \
(((value) >> CBC_STACK_ADJUST_SHIFT) - CBC_STACK_ADJUST_BASE)
// u的意思是没有符号位
#define CBC_NO_FLAG 0x00u
#define CBC_HAS_LITERAL_ARG 0x01u
#define CBC_HAS_LITERAL_ARG2 0x02u
#define CBC_HAS_BYTE_ARG 0x04u
#define CBC_HAS_BRANCH_ARG 0x08u
/* These flags are shared */
#define CBC_FORWARD_BRANCH_ARG 0x10u
#define CBC_POP_STACK_BYTE_ARG 0x10u
CBC_CODE和VM_OC_CODE的转换
VM_OC_CODE = vm_decode_table[BC_CODE] & 111_1111; #define VM_OC_GROUP_MASK 0x7f
/**
* Extract the "group" opcode.
* VM_OC_GROUP_MASK 是7
* 50 & 0x7f = 110010 & 111_1111 = 110010 = 50
* 0 & 0x7f = 0
*/
#define VM_OC_GROUP_GET_INDEX(O) ((O) & VM_OC_GROUP_MASK) 字节码收集
问题
注意
|
内存地址相关简介https://blog.csdn.net/qq_23079443/article/details/81143877
var stackFrame = new StackFrame({
functionName: 'funName',
args: ['args'],
fileName: 'http://localhost:3000/file.js',
lineNumber: 1,
columnNumber: 3288,
isEval: true,
isNative: false,
source: 'ORIGINAL_STACK_LINE'
});
var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel'];
var numericProps = ['columnNumber', 'lineNumber'];
var stringProps = ['fileName', 'functionName', 'source'];
var arrayProps = ['args'];
/**
* Free region node
* 自由区域节点
*/
typedef struct
{
uint32_t next_offset; /**< Offset of next region in list 列表中下一个区域的偏移量 */
uint32_t size; /**< Size of region 区域大小 */
} jmem_heap_free_t;
struct jmem_heap_t
{
jmem_heap_free_t first; /**< first node in free region list 自由区域列表中的第一个节点 */
uint8_t area[JMEM_HEAP_AREA_SIZE]; /**< heap area 头部区域 JMEM_HEAP_AREA_SIZE是512*1024 -8预留8字节*/
};
内部
总结
literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); parser_context.lit_object只是存储当前的literal_object。每个literal_object其实是存在global_heap里面的,详见上面的函数调用了: |
变量管理相关全局变量
|
您好,好巧我最近也在研究JerryScript,有个问题请教下。 |
因为的它的存储结构是jmem_heap_free_t,刚好是8字节吧,(建议关注:#535 这个帖子,以后解读基本以那个为主) |
@cisen hello, 我想请教下,
|
1,build之后是bin二进制文件,相当于多了一个命令,比如 |
说明
https://github.com/pando-project/jerryscript
https://github.com/cisen/sourcecode-jerryscript-
编译过程
环境要求
编译主要靠:
构建过程控制需要用到
vscode要安装
python/c++/cmake插件
为了保证构建脚本运行正确,请保证以下工具系统有提供
awk
bc
find
sed
编译过程
注意:
build.py
一个文件来生成构建命令运行
demo构建过程记录
注意
jerryscript.h
文件的路径:jerry-core\include\jerryscript.h
jerry-core/parser/js/js-lexer.h
的lexer_token_type_t
结构总结
The text was updated successfully, but these errors were encountered: