/* * Copyright (c) 2021-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include namespace JS::Bytecode { Generator::Generator(VM& vm) : m_vm(vm) , m_string_table(make()) , m_identifier_table(make()) , m_regex_table(make()) , m_constants(vm.heap()) { } CodeGenerationErrorOr> Generator::generate(VM& vm, ASTNode const& node, ReadonlySpan parameters, FunctionKind enclosing_function_kind) { Generator generator(vm); for (auto const& parameter : parameters) { if (auto const* identifier = parameter.binding.get_pointer>(); identifier && (*identifier)->is_local()) { generator.set_local_initialized((*identifier)->local_variable_index()); } } generator.switch_to_basic_block(generator.make_block()); SourceLocationScope scope(generator, node); generator.m_enclosing_function_kind = enclosing_function_kind; if (generator.is_in_generator_or_async_function()) { // Immediately yield with no value. auto& start_block = generator.make_block(); generator.emit(Label { start_block }, generator.add_constant(js_undefined())); generator.switch_to_basic_block(start_block); // NOTE: This doesn't have to handle received throw/return completions, as GeneratorObject::resume_abrupt // will not enter the generator from the SuspendedStart state and immediately completes the generator. } auto last_value = TRY(node.generate_bytecode(generator)); if (!generator.current_block().is_terminated() && last_value.has_value()) { generator.emit(last_value.value()); } if (generator.is_in_generator_or_async_function()) { // Terminate all unterminated blocks with yield return for (auto& block : generator.m_root_basic_blocks) { if (block->is_terminated()) continue; generator.switch_to_basic_block(*block); generator.emit(nullptr, generator.add_constant(js_undefined())); } } bool is_strict_mode = false; if (is(node)) is_strict_mode = static_cast(node).is_strict_mode(); else if (is(node)) is_strict_mode = static_cast(node).in_strict_mode(); else if (is(node)) is_strict_mode = static_cast(node).is_strict_mode(); else if (is(node)) is_strict_mode = static_cast(node).is_strict_mode(); size_t size_needed = 0; for (auto& block : generator.m_root_basic_blocks) { size_needed += block->size(); } Vector bytecode; bytecode.ensure_capacity(size_needed); Vector basic_block_start_offsets; basic_block_start_offsets.ensure_capacity(generator.m_root_basic_blocks.size()); HashMap block_offsets; Vector label_offsets; struct UnlinkedExceptionHandlers { size_t start_offset; size_t end_offset; BasicBlock const* handler; BasicBlock const* finalizer; }; Vector unlinked_exception_handlers; for (auto& block : generator.m_root_basic_blocks) { basic_block_start_offsets.append(bytecode.size()); if (block->handler() || block->finalizer()) { unlinked_exception_handlers.append({ .start_offset = bytecode.size(), .end_offset = 0, .handler = block->handler(), .finalizer = block->finalizer(), }); } block_offsets.set(block.ptr(), bytecode.size()); Bytecode::InstructionStreamIterator it(block->instruction_stream()); while (!it.at_end()) { auto& instruction = const_cast(*it); instruction.visit_labels([&](Label& label) { size_t label_offset = bytecode.size() + (bit_cast(&label) - bit_cast(&instruction)); label_offsets.append(label_offset); }); bytecode.append(reinterpret_cast(&instruction), instruction.length()); ++it; } if (!block->is_terminated()) { Op::End end(generator.add_constant(js_undefined())); bytecode.append(reinterpret_cast(&end), end.length()); } if (block->handler() || block->finalizer()) { unlinked_exception_handlers.last().end_offset = bytecode.size(); } } for (auto label_offset : label_offsets) { auto& label = *reinterpret_cast(bytecode.data() + label_offset); auto* block = &label.block(); label.set_address(block_offsets.get(block).value()); } auto executable = vm.heap().allocate_without_realm( move(bytecode), move(generator.m_identifier_table), move(generator.m_string_table), move(generator.m_regex_table), move(generator.m_constants), node.source_code(), generator.m_next_property_lookup_cache, generator.m_next_global_variable_cache, generator.m_next_environment_variable_cache, generator.m_next_register, is_strict_mode); Vector linked_exception_handlers; for (auto& unlinked_handler : unlinked_exception_handlers) { auto start_offset = unlinked_handler.start_offset; auto end_offset = unlinked_handler.end_offset; auto handler_offset = unlinked_handler.handler ? block_offsets.get(unlinked_handler.handler).value() : Optional {}; auto finalizer_offset = unlinked_handler.finalizer ? block_offsets.get(unlinked_handler.finalizer).value() : Optional {}; linked_exception_handlers.append({ start_offset, end_offset, handler_offset, finalizer_offset }); } quick_sort(linked_exception_handlers, [](auto const& a, auto const& b) { return a.start_offset < b.start_offset; }); executable->exception_handlers = move(linked_exception_handlers); executable->basic_block_start_offsets = move(basic_block_start_offsets); return executable; } void Generator::grow(size_t additional_size) { VERIFY(m_current_basic_block); m_current_basic_block->grow(additional_size); } Register Generator::allocate_register() { VERIFY(m_next_register != NumericLimits::max()); return Register { m_next_register++ }; } Generator::SourceLocationScope::SourceLocationScope(Generator& generator, ASTNode const& node) : m_generator(generator) , m_previous_node(m_generator.m_current_ast_node) { m_generator.m_current_ast_node = &node; } Generator::SourceLocationScope::~SourceLocationScope() { m_generator.m_current_ast_node = m_previous_node; } Generator::UnwindContext::UnwindContext(Generator& generator, Optional