/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef struct { njs_vmcode_t operation; size_t size; njs_str_t name; } njs_code_name_t; static njs_code_name_t code_names[] = { { NJS_VMCODE_PUT_ARG, sizeof(njs_vmcode_1addr_t), njs_str("PUT ARG ") }, { NJS_VMCODE_OBJECT, sizeof(njs_vmcode_object_t), njs_str("OBJECT ") }, { NJS_VMCODE_FUNCTION, sizeof(njs_vmcode_function_t), njs_str("FUNCTION ") }, { NJS_VMCODE_ARGUMENTS, sizeof(njs_vmcode_arguments_t), njs_str("ARGUMENTS ") }, { NJS_VMCODE_REGEXP, sizeof(njs_vmcode_regexp_t), njs_str("REGEXP ") }, { NJS_VMCODE_TEMPLATE_LITERAL, sizeof(njs_vmcode_template_literal_t), njs_str("TEMPLATE LITERAL") }, { NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t), njs_str("PROP GET ") }, { NJS_VMCODE_PROPERTY_ATOM_GET, sizeof(njs_vmcode_prop_get_t), njs_str("PROP ATOM GET ") }, { NJS_VMCODE_GLOBAL_GET, sizeof(njs_vmcode_prop_get_t), njs_str("GLOBAL GET ") }, { NJS_VMCODE_PROPERTY_INIT, sizeof(njs_vmcode_prop_set_t), njs_str("PROP INIT ") }, { NJS_VMCODE_PROTO_INIT, sizeof(njs_vmcode_prop_set_t), njs_str("PROTO INIT ") }, { NJS_VMCODE_PROPERTY_SET, sizeof(njs_vmcode_prop_set_t), njs_str("PROP SET ") }, { NJS_VMCODE_PROPERTY_ATOM_SET, sizeof(njs_vmcode_prop_set_t), njs_str("PROP ATOM SET ") }, { NJS_VMCODE_PROPERTY_IN, sizeof(njs_vmcode_3addr_t), njs_str("PROP IN ") }, { NJS_VMCODE_PROPERTY_DELETE, sizeof(njs_vmcode_3addr_t), njs_str("PROP DELETE ") }, { NJS_VMCODE_INSTANCE_OF, sizeof(njs_vmcode_instance_of_t), njs_str("INSTANCE OF ") }, { NJS_VMCODE_FUNCTION_CALL, sizeof(njs_vmcode_function_call_t), njs_str("FUNCTION CALL ") }, { NJS_VMCODE_RETURN, sizeof(njs_vmcode_return_t), njs_str("RETURN ") }, { NJS_VMCODE_STOP, sizeof(njs_vmcode_stop_t), njs_str("STOP ") }, { NJS_VMCODE_INCREMENT, sizeof(njs_vmcode_3addr_t), njs_str("INC ") }, { NJS_VMCODE_DECREMENT, sizeof(njs_vmcode_3addr_t), njs_str("DEC ") }, { NJS_VMCODE_POST_INCREMENT, sizeof(njs_vmcode_3addr_t), njs_str("POST INC ") }, { NJS_VMCODE_POST_DECREMENT, sizeof(njs_vmcode_3addr_t), njs_str("POST DEC ") }, { NJS_VMCODE_DELETE, sizeof(njs_vmcode_2addr_t), njs_str("DELETE ") }, { NJS_VMCODE_VOID, sizeof(njs_vmcode_2addr_t), njs_str("VOID ") }, { NJS_VMCODE_TYPEOF, sizeof(njs_vmcode_2addr_t), njs_str("TYPEOF ") }, { NJS_VMCODE_TO_PROPERTY_KEY, sizeof(njs_vmcode_2addr_t), njs_str("TO PROP KEY ") }, { NJS_VMCODE_TO_PROPERTY_KEY_CHK, sizeof(njs_vmcode_3addr_t), njs_str("TO PROP KEY CHK ") }, { NJS_VMCODE_SET_FUNCTION_NAME, sizeof(njs_vmcode_2addr_t), njs_str("SET FUNC NAME ") }, { NJS_VMCODE_UNARY_PLUS, sizeof(njs_vmcode_2addr_t), njs_str("PLUS ") }, { NJS_VMCODE_UNARY_NEGATION, sizeof(njs_vmcode_2addr_t), njs_str("NEGATION ") }, { NJS_VMCODE_ADDITION, sizeof(njs_vmcode_3addr_t), njs_str("ADD ") }, { NJS_VMCODE_SUBTRACTION, sizeof(njs_vmcode_3addr_t), njs_str("SUBTRACT ") }, { NJS_VMCODE_MULTIPLICATION, sizeof(njs_vmcode_3addr_t), njs_str("MULTIPLY ") }, { NJS_VMCODE_EXPONENTIATION, sizeof(njs_vmcode_3addr_t), njs_str("POWER ") }, { NJS_VMCODE_DIVISION, sizeof(njs_vmcode_3addr_t), njs_str("DIVIDE ") }, { NJS_VMCODE_REMAINDER, sizeof(njs_vmcode_3addr_t), njs_str("REMAINDER ") }, { NJS_VMCODE_LEFT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("LEFT SHIFT ") }, { NJS_VMCODE_RIGHT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("RIGHT SHIFT ") }, { NJS_VMCODE_UNSIGNED_RIGHT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("USGN RIGHT SHIFT") }, { NJS_VMCODE_LOGICAL_NOT, sizeof(njs_vmcode_2addr_t), njs_str("LOGICAL NOT ") }, { NJS_VMCODE_BITWISE_NOT, sizeof(njs_vmcode_2addr_t), njs_str("BINARY NOT ") }, { NJS_VMCODE_BITWISE_AND, sizeof(njs_vmcode_3addr_t), njs_str("BINARY AND ") }, { NJS_VMCODE_BITWISE_XOR, sizeof(njs_vmcode_3addr_t), njs_str("BINARY XOR ") }, { NJS_VMCODE_BITWISE_OR, sizeof(njs_vmcode_3addr_t), njs_str("BINARY OR ") }, { NJS_VMCODE_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("EQUAL ") }, { NJS_VMCODE_NOT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("NOT EQUAL ") }, { NJS_VMCODE_LESS, sizeof(njs_vmcode_3addr_t), njs_str("LESS ") }, { NJS_VMCODE_LESS_OR_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("LESS OR EQUAL ") }, { NJS_VMCODE_GREATER, sizeof(njs_vmcode_3addr_t), njs_str("GREATER ") }, { NJS_VMCODE_GREATER_OR_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("GREATER OR EQUAL") }, { NJS_VMCODE_STRICT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("STRICT EQUAL ") }, { NJS_VMCODE_STRICT_NOT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("STRICT NOT EQUAL") }, { NJS_VMCODE_MOVE, sizeof(njs_vmcode_move_t), njs_str("MOVE ") }, { NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t), njs_str("THROW ") }, { NJS_VMCODE_LET, sizeof(njs_vmcode_variable_t), njs_str("LET ") }, { NJS_VMCODE_LET_UPDATE, sizeof(njs_vmcode_variable_t), njs_str("LET UPDATE ") }, { NJS_VMCODE_INITIALIZATION_TEST, sizeof(njs_vmcode_variable_t), njs_str("INIT TEST ") }, { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t), njs_str("NOT INIT ") }, { NJS_VMCODE_ASSIGNMENT_ERROR, sizeof(njs_vmcode_variable_t), njs_str("ASSIGNMENT ERROR") }, { NJS_VMCODE_DEBUGGER, sizeof(njs_vmcode_debugger_t), njs_str("DEBUGGER ") }, { NJS_VMCODE_AWAIT, sizeof(njs_vmcode_await_t), njs_str("AWAIT ") }, }; void njs_disassembler(njs_vm_t *vm) { njs_uint_t n; njs_vm_code_t *code; code = vm->codes->start; n = vm->codes->items; while (n != 0) { njs_printf("%V:%V\n", &code->file, &code->name); njs_disassemble(code->start, code->end, -1, code->lines); code++; n--; } njs_printf("\n"); } void njs_disassemble(u_char *start, u_char *end, njs_int_t count, njs_arr_t *lines) { u_char *p; uint32_t line; njs_str_t *name; njs_uint_t n; const char *type; njs_vmcode_t operation; njs_code_name_t *code_name; njs_vmcode_jump_t *jump; njs_vmcode_error_t *error; njs_vmcode_1addr_t *code1; njs_vmcode_2addr_t *code2; njs_vmcode_3addr_t *code3; njs_vmcode_array_t *array; njs_vmcode_catch_t *catch; njs_vmcode_import_t *import; njs_vmcode_finally_t *finally; njs_vmcode_try_end_t *try_end; njs_vmcode_try_start_t *try_start; njs_vmcode_cond_jump_t *cond_jump; njs_vmcode_test_jump_t *test_jump; njs_vmcode_prop_next_t *prop_next; njs_vmcode_try_return_t *try_return; njs_vmcode_equal_jump_t *equal; njs_vmcode_prop_foreach_t *prop_foreach; njs_vmcode_method_frame_t *method; njs_vmcode_prop_accessor_t *prop_accessor; njs_vmcode_try_trampoline_t *try_tramp; njs_vmcode_function_frame_t *function; /* * On some 32-bit platform uintptr_t is int and compilers warn * about %l format modifier. size_t has the size as pointer so * there is no run-time overhead. */ p = start; while (((p < end) && (count == -1)) || (count-- > 0)) { operation = *(njs_vmcode_t *) p; line = njs_lookup_line(lines, p - start); if (operation == NJS_VMCODE_ARRAY) { array = (njs_vmcode_array_t *) p; njs_printf("%5uD | %05uz ARRAY %04Xz %uz%s\n", line, p - start, (size_t) array->retval, (size_t) array->length, array->ctor ? " INIT" : ""); p += sizeof(njs_vmcode_array_t); continue; } if (operation == NJS_VMCODE_IF_TRUE_JUMP) { cond_jump = (njs_vmcode_cond_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF TRUE %04Xz %z\n", line, p - start, (size_t) cond_jump->cond, (size_t) cond_jump->offset); p += sizeof(njs_vmcode_cond_jump_t); continue; } if (operation == NJS_VMCODE_IF_FALSE_JUMP) { cond_jump = (njs_vmcode_cond_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF FALSE %04Xz %z\n", line, p - start, (size_t) cond_jump->cond, (size_t) cond_jump->offset); p += sizeof(njs_vmcode_cond_jump_t); continue; } if (operation == NJS_VMCODE_JUMP) { jump = (njs_vmcode_jump_t *) p; njs_printf("%5uD | %05uz JUMP %z\n", line, p - start, (size_t) jump->offset); p += sizeof(njs_vmcode_jump_t); continue; } if (operation == NJS_VMCODE_IF_EQUAL_JUMP) { equal = (njs_vmcode_equal_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF EQUAL %04Xz %04Xz %z\n", line, p - start, (size_t) equal->value1, (size_t) equal->value2, (size_t) equal->offset); p += sizeof(njs_vmcode_equal_jump_t); continue; } if (operation == NJS_VMCODE_TEST_IF_TRUE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz TEST IF TRUE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_TEST_IF_FALSE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz TEST IF FALSE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_COALESCE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz COALESCE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_FUNCTION_FRAME) { function = (njs_vmcode_function_frame_t *) p; njs_printf("%5uD | %05uz FUNCTION FRAME %04Xz %uz%s\n", line, p - start, (size_t) function->name, function->nargs, function->ctor ? " CTOR" : ""); p += sizeof(njs_vmcode_function_frame_t); continue; } if (operation == NJS_VMCODE_METHOD_FRAME) { method = (njs_vmcode_method_frame_t *) p; njs_printf("%5uD | %05uz METHOD FRAME %04Xz %04Xz %uz%s\n", line, p - start, (size_t) method->object, (size_t) method->method, method->nargs, method->ctor ? " CTOR" : ""); p += sizeof(njs_vmcode_method_frame_t); continue; } if (operation == NJS_VMCODE_PROPERTY_FOREACH) { prop_foreach = (njs_vmcode_prop_foreach_t *) p; njs_printf("%5uD | %05uz PROP FOREACH %04Xz %04Xz %z\n", line, p - start, (size_t) prop_foreach->next, (size_t) prop_foreach->object, (size_t) prop_foreach->offset); p += sizeof(njs_vmcode_prop_foreach_t); continue; } if (operation == NJS_VMCODE_PROPERTY_NEXT) { prop_next = (njs_vmcode_prop_next_t *) p; njs_printf("%5uD | %05uz PROP NEXT %04Xz %04Xz %04Xz %z\n", line, p - start, (size_t) prop_next->retval, (size_t) prop_next->object, (size_t) prop_next->next, (size_t) prop_next->offset); p += sizeof(njs_vmcode_prop_next_t); continue; } if (operation == NJS_VMCODE_PROPERTY_ACCESSOR) { prop_accessor = (njs_vmcode_prop_accessor_t *) p; njs_printf("%5uD | %05uz PROP %s ACCESSOR %04Xz %04Xz %04Xz\n", line, p - start, (prop_accessor->type == NJS_OBJECT_PROP_GETTER) ? "GET" : "SET", (size_t) prop_accessor->value, (size_t) prop_accessor->object, (size_t) prop_accessor->property); p += sizeof(njs_vmcode_prop_accessor_t); continue; } if (operation == NJS_VMCODE_IMPORT) { import = (njs_vmcode_import_t *) p; njs_printf("%5uD | %05uz IMPORT %04Xz %V\n", line, p - start, (size_t) import->retval, &import->module->name); p += sizeof(njs_vmcode_import_t); continue; } if (operation == NJS_VMCODE_TRY_START) { try_start = (njs_vmcode_try_start_t *) p; njs_printf("%5uD | %05uz TRY START %04Xz %04Xz %z\n", line, p - start, (size_t) try_start->exception_value, (size_t) try_start->exit_value, (size_t) try_start->offset); p += sizeof(njs_vmcode_try_start_t); continue; } if (operation == NJS_VMCODE_TRY_BREAK) { try_tramp = (njs_vmcode_try_trampoline_t *) p; njs_printf("%5uD | %05uz TRY BREAK %04Xz %z\n", line, p - start, (size_t) try_tramp->exit_value, (size_t) try_tramp->offset); p += sizeof(njs_vmcode_try_trampoline_t); continue; } if (operation == NJS_VMCODE_TRY_CONTINUE) { try_tramp = (njs_vmcode_try_trampoline_t *) p; njs_printf("%5uD | %05uz TRY CONTINUE %04Xz %z\n", line, p - start, (size_t) try_tramp->exit_value, (size_t) try_tramp->offset); p += sizeof(njs_vmcode_try_trampoline_t); continue; } if (operation == NJS_VMCODE_TRY_RETURN) { try_return = (njs_vmcode_try_return_t *) p; njs_printf("%5uD | %05uz TRY RETURN %04Xz %04Xz %z\n", line, p - start, (size_t) try_return->save, (size_t) try_return->retval, (size_t) try_return->offset); p += sizeof(njs_vmcode_try_return_t); continue; } if (operation == NJS_VMCODE_CATCH) { catch = (njs_vmcode_catch_t *) p; njs_printf("%5uD | %05uz CATCH %04Xz %z\n", line, p - start, (size_t) catch->exception, (size_t) catch->offset); p += sizeof(njs_vmcode_catch_t); continue; } if (operation == NJS_VMCODE_TRY_END) { try_end = (njs_vmcode_try_end_t *) p; njs_printf("%5uD | %05uz TRY END %z\n", line, p - start, (size_t) try_end->offset); p += sizeof(njs_vmcode_try_end_t); continue; } if (operation == NJS_VMCODE_FINALLY) { finally = (njs_vmcode_finally_t *) p; njs_printf("%5uD | %05uz TRY FINALLY %04Xz %04Xz %z %z\n", line, p - start, (size_t) finally->retval, (size_t) finally->exit_value, (size_t) finally->continue_offset, (size_t) finally->break_offset); p += sizeof(njs_vmcode_finally_t); continue; } if (operation == NJS_VMCODE_ERROR) { error = (njs_vmcode_error_t *) p; switch (error->type) { case NJS_OBJ_TYPE_REF_ERROR: type = "REFERENCE"; break; case NJS_OBJ_TYPE_TYPE_ERROR: type = "TYPE"; break; case NJS_OBJ_TYPE_ERROR: default: type = ""; } njs_printf("%5uD | %05uz %s ERROR\n", line, p - start, type); p += sizeof(njs_vmcode_error_t); continue; } code_name = code_names; n = njs_nitems(code_names); do { if (operation == code_name->operation) { name = &code_name->name; if (code_name->size == sizeof(njs_vmcode_3addr_t)) { code3 = (njs_vmcode_3addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz %04Xz %04Xz\n", line, p - start, name->length, name->start, (size_t) code3->dst, (size_t) code3->src1, (size_t) code3->src2); } else if (code_name->size == sizeof(njs_vmcode_2addr_t)) { code2 = (njs_vmcode_2addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz %04Xz\n", line, p - start, name->length, name->start, (size_t) code2->dst, (size_t) code2->src); } else if (code_name->size == sizeof(njs_vmcode_1addr_t)) { code1 = (njs_vmcode_1addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz\n", line, p - start, name->length, name->start, (size_t) code1->index); } p += code_name->size; goto next; } code_name++; n--; } while (n != 0); njs_printf("%5uD | %05uz UNKNOWN %04Xz\n", line, p - start, (size_t) (uintptr_t) operation); p += sizeof(njs_vmcode_t); next: continue; } }