当前位置: 首页 > 工具软件 > QuickJS > 使用案例 >

QuickJS 操作码

司徒博容
2023-12-01

QuickJS - 操作码

字节码

QuickJS执行JavaScript代码时,先将源码解析成字节码,然后再执行字节码。而字节码由操作码和操作数组成。操作码有点类似于汇编语言的指令,决定”CPU“执行何种操作。操作数就是操作码将要操作的数据。

操作码的生成

操作码在quickjs-opcode.h中定义。

/*
 * QuickJS opcode definitions
 * 
 * Copyright (c) 2017-2018 Fabrice Bellard
 * Copyright (c) 2017-2018 Charlie Gordon
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(none_var_ref)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(var_ref)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(atom)
FMT(atom_u8)
FMT(atom_u16)
FMT(atom_label_u8)
FMT(atom_label_u16)
FMT(label_u16)
#undef FMT
#endif /* FMT */

#ifdef DEF

#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif

DEF(invalid, 1, 0, 0, none) /* never emitted */

/* push values */
DEF(       push_i32, 5, 0, 1, i32)
DEF(     push_const, 5, 0, 1, const)
DEF(       fclosure, 5, 0, 1, const) /* must follow push_const */
DEF(push_atom_value, 5, 0, 1, atom)
DEF( private_symbol, 5, 0, 1, atom)
DEF(      undefined, 1, 0, 1, none)
DEF(           null, 1, 0, 1, none)
DEF(      push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF(     push_false, 1, 0, 1, none)
DEF(      push_true, 1, 0, 1, none)
DEF(         object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF(           rest, 3, 0, 1, u16) /* only used at the start of a function */

DEF(           drop, 1, 1, 0, none) /* a -> */
DEF(            nip, 1, 2, 1, none) /* a b -> b */
DEF(           nip1, 1, 3, 2, none) /* a b c -> b c */
DEF(            dup, 1, 1, 2, none) /* a -> a a */
DEF(           dup1, 1, 2, 3, none) /* a b -> a a b */
DEF(           dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF(           dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF(        insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF(        insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF(        insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF(          perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF(          perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF(          perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF(           swap, 1, 2, 2, none) /* a b -> b a */
DEF(          swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF(          rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF(          rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF(          rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF(          rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */

DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
DEF(           call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF(      tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF(    call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF(     array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF(          apply, 3, 3, 1, u16)
DEF(         return, 1, 1, 0, none)
DEF(   return_undef, 1, 0, 0, none)
DEF(check_ctor_return, 1, 1, 2, none)
DEF(     check_ctor, 1, 0, 0, none)
DEF(    check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
DEF(      add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
DEF(   return_async, 1, 1, 0, none)
DEF(          throw, 1, 1, 0, none)
DEF(    throw_error, 6, 0, 0, atom_u8)
DEF(           eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
DEF(     apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
DEF(         regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
                                       bytecode string */
DEF(      get_super, 1, 1, 1, none)
DEF(         import, 1, 1, 1, none) /* dynamic module import */

DEF(      check_var, 5, 0, 1, atom) /* check if a variable exists */
DEF(  get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
DEF(        get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
DEF(        put_var, 5, 1, 0, atom) /* must come after get_var */
DEF(   put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */

DEF(  get_ref_value, 1, 2, 3, none)
DEF(  put_ref_value, 1, 3, 0, none)

DEF(     define_var, 6, 0, 0, atom_u8)
DEF(check_define_var, 6, 0, 0, atom_u8)
DEF(    define_func, 6, 1, 0, atom_u8)
DEF(      get_field, 5, 1, 1, atom)
DEF(     get_field2, 5, 1, 2, atom)
DEF(      put_field, 5, 2, 0, atom)
DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
DEF(   get_array_el, 1, 2, 1, none)
DEF(  get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF(   put_array_el, 1, 3, 0, none)
DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
DEF(   define_field, 5, 2, 1, atom)
DEF(       set_name, 5, 1, 1, atom)
DEF(set_name_computed, 1, 2, 2, none)
DEF(      set_proto, 1, 2, 1, none)
DEF(set_home_object, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF(         append, 1, 3, 2, none) /* append enumerated object, update length */
DEF(copy_data_properties, 2, 3, 3, u8)
DEF(  define_method, 6, 2, 1, atom_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF(   define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
DEF(   define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */

DEF(        get_loc, 3, 0, 1, loc)
DEF(        put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF(        set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF(        get_arg, 3, 0, 1, arg)
DEF(        put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF(        set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF(    get_var_ref, 3, 0, 1, var_ref) 
DEF(    put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
DEF(    set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF(  get_loc_check, 3, 0, 1, loc)
DEF(  put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF(  put_loc_check_init, 3, 1, 0, loc)
DEF(get_var_ref_check, 3, 0, 1, var_ref) 
DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
DEF(      close_loc, 3, 0, 0, loc)
DEF(       if_false, 5, 1, 0, label)
DEF(        if_true, 5, 1, 0, label) /* must come after if_false */
DEF(           goto, 5, 0, 0, label) /* must come after if_true */
DEF(          catch, 5, 0, 1, label)
DEF(          gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF(            ret, 1, 1, 0, none) /* used to return from the finally block */

DEF(      to_object, 1, 1, 1, none)
//DEF(      to_string, 1, 1, 1, none)
DEF(     to_propkey, 1, 1, 1, none)
DEF(    to_propkey2, 1, 2, 2, none)

DEF(   with_get_var, 10, 1, 0, atom_label_u8)     /* must be in the same order as scope_xxx */
DEF(   with_put_var, 10, 2, 1, atom_label_u8)     /* must be in the same order as scope_xxx */
DEF(with_delete_var, 10, 1, 0, atom_label_u8)     /* must be in the same order as scope_xxx */
DEF(  with_make_ref, 10, 1, 0, atom_label_u8)     /* must be in the same order as scope_xxx */
DEF(   with_get_ref, 10, 1, 0, atom_label_u8)     /* must be in the same order as scope_xxx */
DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)

DEF(   make_loc_ref, 7, 0, 2, atom_u16)
DEF(   make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF(   make_var_ref, 5, 0, 2, atom)

DEF(   for_in_start, 1, 1, 1, none)
DEF(   for_of_start, 1, 1, 3, none)
DEF(for_await_of_start, 1, 1, 3, none)
DEF(    for_in_next, 1, 1, 3, none)
DEF(    for_of_next, 2, 3, 5, u8)
DEF(iterator_check_object, 1, 1, 1, none)
DEF(iterator_get_value_done, 1, 1, 2, none)
DEF( iterator_close, 1, 3, 0, none)
DEF(iterator_close_return, 1, 4, 4, none)
DEF(  iterator_next, 1, 4, 4, none)
DEF(  iterator_call, 2, 4, 5, u8)
DEF(  initial_yield, 1, 0, 0, none)
DEF(          yield, 1, 1, 2, none)
DEF(     yield_star, 1, 1, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF(          await, 1, 1, 1, none)

/* arithmetic/logic operations */
DEF(            neg, 1, 1, 1, none)
DEF(           plus, 1, 1, 1, none)
DEF(            dec, 1, 1, 1, none)
DEF(            inc, 1, 1, 1, none)
DEF(       post_dec, 1, 1, 2, none)
DEF(       post_inc, 1, 1, 2, none)
DEF(        dec_loc, 2, 0, 0, loc8)
DEF(        inc_loc, 2, 0, 0, loc8)
DEF(        add_loc, 2, 1, 0, loc8)
DEF(            not, 1, 1, 1, none)
DEF(           lnot, 1, 1, 1, none)
DEF(         typeof, 1, 1, 1, none)
DEF(         delete, 1, 2, 1, none)
DEF(     delete_var, 5, 0, 1, atom)

DEF(            mul, 1, 2, 1, none)
DEF(            div, 1, 2, 1, none)
DEF(            mod, 1, 2, 1, none)
DEF(            add, 1, 2, 1, none)
DEF(            sub, 1, 2, 1, none)
DEF(            pow, 1, 2, 1, none)
DEF(            shl, 1, 2, 1, none)
DEF(            sar, 1, 2, 1, none)
DEF(            shr, 1, 2, 1, none)
DEF(             lt, 1, 2, 1, none)
DEF(            lte, 1, 2, 1, none)
DEF(             gt, 1, 2, 1, none)
DEF(            gte, 1, 2, 1, none)
DEF(     instanceof, 1, 2, 1, none)
DEF(             in, 1, 2, 1, none)
DEF(             eq, 1, 2, 1, none)
DEF(            neq, 1, 2, 1, none)
DEF(      strict_eq, 1, 2, 1, none)
DEF(     strict_neq, 1, 2, 1, none)
DEF(            and, 1, 2, 1, none)
DEF(            xor, 1, 2, 1, none)
DEF(             or, 1, 2, 1, none)
DEF(is_undefined_or_null, 1, 1, 1, none)
#ifdef CONFIG_BIGNUM
DEF(      mul_pow10, 1, 2, 1, none)
DEF(       math_mod, 1, 2, 1, none)
#endif
/* must be the last non short and non temporary opcode */
DEF(            nop, 1, 0, 0, none) 

/* temporary opcodes: never emitted in the final bytecode */

def(    enter_scope, 3, 0, 0, u16)  /* emitted in phase 1, removed in phase 2 */
def(    leave_scope, 3, 0, 0, u16)  /* emitted in phase 1, removed in phase 2 */

def(          label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */

def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(  scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(  scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
def(  scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */

def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
    
def(       line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */

#if SHORT_OPCODES
DEF(    push_minus1, 1, 0, 1, none_int)
DEF(         push_0, 1, 0, 1, none_int)
DEF(         push_1, 1, 0, 1, none_int)
DEF(         push_2, 1, 0, 1, none_int)
DEF(         push_3, 1, 0, 1, none_int)
DEF(         push_4, 1, 0, 1, none_int)
DEF(         push_5, 1, 0, 1, none_int)
DEF(         push_6, 1, 0, 1, none_int)
DEF(         push_7, 1, 0, 1, none_int)
DEF(        push_i8, 2, 0, 1, i8)
DEF(       push_i16, 3, 0, 1, i16)
DEF(    push_const8, 2, 0, 1, const8)
DEF(      fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)

DEF(       get_loc8, 2, 0, 1, loc8)
DEF(       put_loc8, 2, 1, 0, loc8)
DEF(       set_loc8, 2, 1, 1, loc8)

DEF(       get_loc0, 1, 0, 1, none_loc)
DEF(       get_loc1, 1, 0, 1, none_loc)
DEF(       get_loc2, 1, 0, 1, none_loc)
DEF(       get_loc3, 1, 0, 1, none_loc)
DEF(       put_loc0, 1, 1, 0, none_loc)
DEF(       put_loc1, 1, 1, 0, none_loc)
DEF(       put_loc2, 1, 1, 0, none_loc)
DEF(       put_loc3, 1, 1, 0, none_loc)
DEF(       set_loc0, 1, 1, 1, none_loc)
DEF(       set_loc1, 1, 1, 1, none_loc)
DEF(       set_loc2, 1, 1, 1, none_loc)
DEF(       set_loc3, 1, 1, 1, none_loc)
DEF(       get_arg0, 1, 0, 1, none_arg)
DEF(       get_arg1, 1, 0, 1, none_arg)
DEF(       get_arg2, 1, 0, 1, none_arg)
DEF(       get_arg3, 1, 0, 1, none_arg)
DEF(       put_arg0, 1, 1, 0, none_arg)
DEF(       put_arg1, 1, 1, 0, none_arg)
DEF(       put_arg2, 1, 1, 0, none_arg)
DEF(       put_arg3, 1, 1, 0, none_arg)
DEF(       set_arg0, 1, 1, 1, none_arg)
DEF(       set_arg1, 1, 1, 1, none_arg)
DEF(       set_arg2, 1, 1, 1, none_arg)
DEF(       set_arg3, 1, 1, 1, none_arg)
DEF(   get_var_ref0, 1, 0, 1, none_var_ref)
DEF(   get_var_ref1, 1, 0, 1, none_var_ref)
DEF(   get_var_ref2, 1, 0, 1, none_var_ref)
DEF(   get_var_ref3, 1, 0, 1, none_var_ref)
DEF(   put_var_ref0, 1, 1, 0, none_var_ref)
DEF(   put_var_ref1, 1, 1, 0, none_var_ref)
DEF(   put_var_ref2, 1, 1, 0, none_var_ref)
DEF(   put_var_ref3, 1, 1, 0, none_var_ref)
DEF(   set_var_ref0, 1, 1, 1, none_var_ref)
DEF(   set_var_ref1, 1, 1, 1, none_var_ref)
DEF(   set_var_ref2, 1, 1, 1, none_var_ref)
DEF(   set_var_ref3, 1, 1, 1, none_var_ref)

DEF(     get_length, 1, 1, 1, none)

DEF(      if_false8, 2, 1, 0, label8)
DEF(       if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF(          goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF(         goto16, 3, 0, 0, label16)

DEF(          call0, 1, 1, 1, npopx)
DEF(          call1, 1, 1, 1, npopx)
DEF(          call2, 1, 1, 1, npopx)
DEF(          call3, 1, 1, 1, npopx)

DEF(   is_undefined, 1, 1, 1, none)
DEF(        is_null, 1, 1, 1, none)
DEF(typeof_is_undefined, 1, 1, 1, none)
DEF( typeof_is_function, 1, 1, 1, none)
#endif

#undef DEF
#undef def
#endif  /* DEF */

只看这个文件,并不能很好的理解操作码是如何生成的。因为,这个文件中并未定义FMT和DEF宏定义。而QuickJS代码巧妙的使用了宏定义。通过更改宏定义的定义,实现同一份代码完成多个定义。这招叫做一鱼多吃,实在是妙。
字节码的定义在quickjs.c文件中。

typedef struct JSOpCode {
#ifdef DUMP_BYTECODE
    const char *name;
#endif
    uint8_t size; /* in bytes */
    /* the opcodes remove n_pop items from the top of the stack, then
       pushes n_push items */
    uint8_t n_pop;
    uint8_t n_push;
    uint8_t fmt;
} JSOpCode;

static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
#define FMT(f)
#ifdef DUMP_BYTECODE
#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
#else
#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
#endif
#include "quickjs-opcode.h"
#undef DEF
#undef FMT
};

程序的开始,定义了一个JSOpCode结构体,每个操作码使用JSOpCode表示。这个结构体中,定义了操作码名称、操作码加其后面操作数总长度(单位是byte,如:如果是5,则表示操作码加操作数总共是5bytpe,代码执行时根据这个来做PC偏移的)、该操作码执行时会出栈或入栈多少个项(根据其操作数的个数决定)以及其操作对象的格式。
当QuickJS被编译器编译时,首先会对其源码进行预编译。在预编译过程中,根据宏定义,将会完成对opcode_info的定义。预编译之后,opcode_info将会变成下面代码:

static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
    {"invalid",1,0,0,OP_FMT_none},
    {"push_i32",5,0,1,OP_FMT_i32},
    ······
    {"typeof_is_function", 1, 1, 1, OP_FMT_none}
};

那么为什么是这样的呢?
解释前,先复习下宏定义。宏定义的存活于编译的预处理中。 宏定义中有两个运算符:#和##。#运算符将一个宏的参数转换为字符串字面量。##运算符可以将两个记号(例如标识符)“粘”在一起,成为一个记号。(##运算符被称为“记号粘合”)。举个例子:

#define STR(s) #s
#define CONS(a,b) int(a##e##b)
int main(){
    printf(STR(vck));
    printf("%d/n", CONS(2,3));
    return 0;
}

以上代码预处理之后,将会变成这样

int main(){
    printf("vck"); 
    printf("%d/n", 2e3);
    return 0;
}

我们可以看到,STR宏定义的作用,就是将其参数转换成字符串字面量;而CONS宏定义的作用就是将其参数连接起来。
回到opcode_info的定义上来。首先,先定义了它作为一个数组。数组的长度是:OP_COUNT + (OP_TEMP_END - OP_TEMP_START) 。然后就是数组的定义。首先定义FMT(f)为空,然后根据是否定义了DUMP_BYTECODE来对DEF进行不同的定义。DEF的这两个不同定义的区别是:是否存在name这个字段。两种定义摘出来如下

//第一种
#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
//第二种
#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },

这两个宏定义可以看出来,都是定义一个结构体。这个结构体,就是我们文章一开始说的操作码–JSOpCode。
接下来,通过 #include “quickjs-opcode.h” 引入了操作码定义文件。在这里,为了便于解释,我们选择DEF的第一种宏定义来进行解释。
在操作码定义头文件中,首先是使用FMT来定义一些操作数格式。当然,在这个场景中,我们的FMT定义为空,因此,FMT定义的所有语句都是无效的,不会生成代码。
接下来是使用DEF定义了一些语句。根据它的宏定义含义,那么第一行定义(如下:)

DEF(invalid, 1, 0, 0, none)

将会生成如下代码:

{"invalid", 1, 0, 0, OP_FMT_none},

同样,第二行代码(如下:)

DEF(       push_i32, 5, 0, 1, i32)

会生成如下代码:

{"push_i32", 5, 0, 1, OP_FMT_i32},

同样,以此类推,预编译之后最终将会生成如下的代码

static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = 
{
    {"invalid",1,0,0,OP_FMT_none},
    {"push_i32",5,0,1,OP_FMT_i32},
    ······
    {"typeof_is_function", 1, 1, 1, OP_FMT_none}
}

操作数格式

在操作码中,定义了fmt字段。该字段的含义是标明操作码操作的操作数是什么格式。比如:如果是32bit的整数,则其值为OP_FMT_i32。OP_FMT_i32是怎么来的呢?也是通过宏定义生成的。它是一个枚举类型。定义代码如下:

//定义opcodeFormat枚举,例如:OP_FMT_none  OP_FMT_none_int OP_FMT_none_loc
typedef enum OPCodeFormat {
#define FMT(f) OP_FMT_ ## f,
#define DEF(id, size, n_pop, n_push, f)
#include "quickjs-opcode.h"
#undef DEF
#undef FMT
} OPCodeFormat;

在这个枚举定义中,我们首先定义了FMT的定义,其定义为

#define FMT(f) OP_FMT_ ## f,

接下来,这次与上面操作码的定义不同,定义DEF为空。
#define DEF(id, size, n_pop, n_push, f)

同样,通过引入 #include “quickjs-opcode.h” 头文件来定义操作数格式。这次,我们就不仔细解释了。预处理之后的代码如下:

typedef enum OPCodeFormat {
    OP_FMT_none,
    OP_FMT_none_int,
    ......
    OP_FMT_i32,
    ......
    OP_FMT_label_u16
} OPCodeFormat;

操作码的种类

操作码决定了”CPU“做何种操作。我们可以将这些操作码分成如下几类:

变量操作相关指令

  • define_var, define_func, define_class, define_field, define_method…
  • (get | put | set)_(loc | arg | var_ref | var | field | array_el…)

调用相关指令

  • call, tail_call, call_method, tail_call_method, return…

跳转相关指令

  • goto, if_false, if_true, label…

指令变换相关指令

  • drop, nip, dup, insert, perm, swap, rot…

数据相关指令

  • push, object, array_from…

欢迎转载,请注明出处

 类似资料: