/* * Copyright (c) 2026, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern bool JS::g_dump_ast; extern bool JS::g_dump_ast_use_color; using namespace JS::FFI; namespace JS::RustIntegration { // --- Shared helpers --- static Utf16View utf16_view_from_bytes(uint16_t const* data, size_t len) { if (len == 0) return {}; return Utf16View { reinterpret_cast(data), len }; } static Utf16FlyString utf16_fly_from(uint16_t const* data, size_t len) { return Utf16FlyString::from_utf16(utf16_view_from_bytes(data, len)); } static Utf16FlyString utf16_fly_from_raw(uint16_t const* data, size_t len) { if (len == 0) return {}; return Utf16FlyString::from_utf16(utf16_view_from_bytes(data, len)); } static Utf16String utf16_from_raw(uint16_t const* data, size_t len) { if (len == 0) return {}; return Utf16String::from_utf16(utf16_view_from_bytes(data, len)); } // --- Error collection callbacks --- // Collects parse errors as a Vector (for Script/Module compilation). static void collect_parse_errors(void* ctx, uint8_t const* message, size_t message_len, uint32_t line, uint32_t column) { auto& errors = *static_cast*>(ctx); errors.append({ MUST(String::from_utf8({ message, message_len })), Position { line, column, 0 }, }); } // Collects a single parse error as a formatted String (for eval/dynamic function compilation). static void collect_single_parse_error(void* ctx, uint8_t const* message, size_t message_len, uint32_t line, uint32_t column) { auto& error_message = *static_cast(ctx); if (error_message.is_empty()) error_message = MUST(String::formatted("{} (line: {}, column: {})", MUST(String::from_utf8({ message, message_len })), line, column)); } // --- Script GDI builder and callbacks --- struct ScriptGdiBuilder { ScriptResult result; }; } namespace JS::FFI { extern "C" void script_gdi_push_lexical_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->result.lexical_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void script_gdi_push_var_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->result.var_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void script_gdi_push_function(void* ctx, void* sfd_ptr, uint16_t const* name, size_t len) { auto& builder = *static_cast(ctx); auto fn_name = JS::RustIntegration::utf16_fly_from(name, len); builder.result.declared_function_names.set(fn_name); auto& sfd = *static_cast(sfd_ptr); builder.result.functions_to_initialize.append({ sfd, move(fn_name) }); } extern "C" void script_gdi_push_var_scoped_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->result.var_scoped_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void script_gdi_push_annex_b_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->result.annex_b_candidate_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void script_gdi_push_lexical_binding(void* ctx, uint16_t const* name, size_t len, bool is_constant) { static_cast(ctx)->result.lexical_bindings.append({ JS::RustIntegration::utf16_fly_from(name, len), is_constant }); } } // --- Eval GDI builder and callbacks --- namespace JS::RustIntegration { struct EvalGdiBuilder { GC::Ptr executable; bool is_strict_mode { false }; Vector var_names; Vector functions_to_initialize; HashTable declared_function_names; Vector var_scoped_names; Vector annex_b_candidate_names; Vector lexical_bindings; EvalResult to_result() { EvalResult result; result.executable = executable; result.is_strict_mode = is_strict_mode; result.declaration_data.var_names = move(var_names); result.declaration_data.functions_to_initialize = move(functions_to_initialize); result.declaration_data.declared_function_names = move(declared_function_names); result.declaration_data.var_scoped_names = move(var_scoped_names); result.declaration_data.annex_b_candidate_names = move(annex_b_candidate_names); result.declaration_data.lexical_bindings = move(lexical_bindings); return result; } }; } namespace JS::FFI { extern "C" void eval_gdi_set_strict(void* ctx, bool is_strict) { static_cast(ctx)->is_strict_mode = is_strict; } extern "C" void eval_gdi_push_var_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->var_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void eval_gdi_push_function(void* ctx, void* sfd_ptr, uint16_t const* name, size_t len) { auto& builder = *static_cast(ctx); auto fn_name = JS::RustIntegration::utf16_fly_from(name, len); builder.declared_function_names.set(fn_name); auto& sfd = *static_cast(sfd_ptr); builder.functions_to_initialize.append({ sfd, move(fn_name) }); } extern "C" void eval_gdi_push_var_scoped_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->var_scoped_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void eval_gdi_push_annex_b_name(void* ctx, uint16_t const* name, size_t len) { static_cast(ctx)->annex_b_candidate_names.append(JS::RustIntegration::utf16_fly_from(name, len)); } extern "C" void eval_gdi_push_lexical_binding(void* ctx, uint16_t const* name, size_t len, bool is_constant) { static_cast(ctx)->lexical_bindings.append({ JS::RustIntegration::utf16_fly_from(name, len), is_constant }); } } // --- Module builder and callbacks --- namespace JS::RustIntegration { struct ModuleBuilder { ModuleResult result; }; static Vector attributes_from_ffi(FFIUtf16Slice const* keys, FFIUtf16Slice const* values, size_t count) { Vector attributes; for (size_t i = 0; i < count; ++i) attributes.empend(utf16_from_raw(keys[i].data, keys[i].length), utf16_from_raw(values[i].data, values[i].length)); return attributes; } static Optional module_request_from_ffi(uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { if (specifier == nullptr || specifier_len == 0) return {}; auto attributes = attributes_from_ffi(attribute_keys, attribute_values, attribute_count); if (attributes.is_empty()) return ModuleRequest { utf16_fly_from_raw(specifier, specifier_len) }; return ModuleRequest { utf16_fly_from_raw(specifier, specifier_len), move(attributes) }; } } extern "C" { static void module_set_has_top_level_await(void* ctx, bool value) { static_cast(ctx)->result.has_top_level_await = value; } static void module_push_import_entry(void* ctx, uint16_t const* import_name, size_t import_name_len, bool is_namespace, uint16_t const* local_name, size_t local_name_len, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { auto* builder = static_cast(ctx); Optional import_name_opt; if (!is_namespace) import_name_opt = JS::RustIntegration::utf16_fly_from_raw(import_name, import_name_len); JS::ImportEntry entry { move(import_name_opt), JS::RustIntegration::utf16_fly_from_raw(local_name, local_name_len) }; entry.m_module_request = JS::RustIntegration::module_request_from_ffi(specifier, specifier_len, attribute_keys, attribute_values, attribute_count); builder->result.import_entries.append(move(entry)); } static void module_push_export_entry(Vector& list, uint8_t kind, uint16_t const* export_name, size_t export_name_len, uint16_t const* local_or_import_name, size_t local_or_import_name_len, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { Optional en; if (export_name) en = JS::RustIntegration::utf16_fly_from_raw(export_name, export_name_len); Optional lin; if (local_or_import_name) lin = JS::RustIntegration::utf16_fly_from_raw(local_or_import_name, local_or_import_name_len); JS::ExportEntry entry { static_cast(kind), move(en), move(lin) }; entry.m_module_request = JS::RustIntegration::module_request_from_ffi(specifier, specifier_len, attribute_keys, attribute_values, attribute_count); list.append(move(entry)); } static void module_push_local_export(void* ctx, uint8_t kind, uint16_t const* export_name, size_t export_name_len, uint16_t const* local_or_import_name, size_t local_or_import_name_len, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { module_push_export_entry(static_cast(ctx)->result.local_export_entries, kind, export_name, export_name_len, local_or_import_name, local_or_import_name_len, specifier, specifier_len, attribute_keys, attribute_values, attribute_count); } static void module_push_indirect_export(void* ctx, uint8_t kind, uint16_t const* export_name, size_t export_name_len, uint16_t const* local_or_import_name, size_t local_or_import_name_len, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { module_push_export_entry(static_cast(ctx)->result.indirect_export_entries, kind, export_name, export_name_len, local_or_import_name, local_or_import_name_len, specifier, specifier_len, attribute_keys, attribute_values, attribute_count); } static void module_push_star_export(void* ctx, uint8_t kind, uint16_t const* export_name, size_t export_name_len, uint16_t const* local_or_import_name, size_t local_or_import_name_len, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { module_push_export_entry(static_cast(ctx)->result.star_export_entries, kind, export_name, export_name_len, local_or_import_name, local_or_import_name_len, specifier, specifier_len, attribute_keys, attribute_values, attribute_count); } static void module_push_requested_module(void* ctx, uint16_t const* specifier, size_t specifier_len, FFIUtf16Slice const* attribute_keys, FFIUtf16Slice const* attribute_values, size_t attribute_count) { auto* builder = static_cast(ctx); auto attributes = JS::RustIntegration::attributes_from_ffi(attribute_keys, attribute_values, attribute_count); if (attributes.is_empty()) builder->result.requested_modules.empend(JS::RustIntegration::utf16_fly_from_raw(specifier, specifier_len)); else builder->result.requested_modules.empend(JS::RustIntegration::utf16_fly_from_raw(specifier, specifier_len), move(attributes)); } static void module_set_default_export_binding(void* ctx, uint16_t const* name, size_t name_len) { static_cast(ctx)->result.default_export_binding_name = JS::RustIntegration::utf16_fly_from_raw(name, name_len); } static void module_push_var_name(void* ctx, uint16_t const* name, size_t name_len) { static_cast(ctx)->result.var_declared_names.append(JS::RustIntegration::utf16_fly_from_raw(name, name_len)); } static void module_push_function(void* ctx, void* sfd_ptr, uint16_t const* name, size_t name_len) { auto& shared = *static_cast(sfd_ptr); static_cast(ctx)->result.functions_to_initialize.append({ shared, JS::RustIntegration::utf16_fly_from_raw(name, name_len) }); } static void module_push_lexical_binding(void* ctx, uint16_t const* name, size_t name_len, bool is_constant, int32_t function_index) { static_cast(ctx)->result.lexical_bindings.append({ .name = JS::RustIntegration::utf16_fly_from_raw(name, name_len), .is_constant = is_constant, .function_index = function_index, }); } } // extern "C" // --- Builtin file callback --- namespace JS::RustIntegration { static void collect_builtin_function(void* ctx, void* sfd_ptr, uint16_t const*, size_t) { auto& list = *static_cast>*>(ctx); list.append(*static_cast(sfd_ptr)); } // --- Compile functions --- bool rust_pipeline_available() { return true; } ParsedProgram* parse_program(u16 const* utf16_data, size_t length_in_code_units, ProgramType type, size_t line_number_offset) { return rust_parse_program(utf16_data, length_in_code_units, static_cast(type), line_number_offset, g_dump_ast, g_dump_ast_use_color); } bool parsed_program_has_errors(ParsedProgram const* parsed) { return rust_parsed_program_has_errors(const_cast(parsed)); } void free_parsed_program(ParsedProgram* parsed) { rust_free_parsed_program(parsed); } Optional>> compile_parsed_script(ParsedProgram* parsed, NonnullRefPtr source_code, Realm& realm) { if (!parsed) return {}; if (rust_parsed_program_has_errors(parsed)) { Vector parse_errors; rust_parsed_program_take_errors(parsed, &parse_errors, collect_parse_errors); rust_free_parsed_program(parsed); return parse_errors; } auto length = source_code->length_in_code_units(); GC::DeferGC defer_gc(realm.vm().heap()); ScriptGdiBuilder builder; void* exec_ptr = rust_compile_parsed_script(parsed, &realm.vm(), source_code.ptr(), &builder, length); if (!exec_ptr) return Vector {}; builder.result.executable = static_cast(exec_ptr); return builder.result; } Optional>> compile_script(StringView source_text, Realm& realm, StringView filename, size_t line_number_offset) { auto source_code = SourceCode::create( String::from_utf8(filename).release_value_but_fixme_should_propagate_errors(), Utf16String::from_utf8(source_text)); auto const* source_ptr = source_code->utf16_data(); auto length = source_code->length_in_code_units(); auto* parsed = rust_parse_program(source_ptr, length, static_cast(ProgramType::Script), line_number_offset, g_dump_ast, g_dump_ast_use_color); return compile_parsed_script(parsed, source_code, realm); } Optional> compile_eval( PrimitiveString& code_string, VM& vm, CallerMode strict_caller, bool in_function, bool in_method, bool in_derived_constructor, bool in_class_field_initializer) { auto source_code = SourceCode::create({}, code_string.utf16_string()); auto const& code_view = source_code->code_view(); auto length = code_view.length_in_code_units(); GC::DeferGC defer_gc(vm.heap()); EvalGdiBuilder builder; String parse_error; auto const* source_ptr = source_code->utf16_data(); void* exec_ptr = rust_compile_eval(source_ptr, length, &vm, source_code.ptr(), &builder, strict_caller == CallerMode::Strict, in_function, in_method, in_derived_constructor, in_class_field_initializer, &parse_error, collect_single_parse_error, nullptr, nullptr); if (!exec_ptr) return parse_error; builder.executable = static_cast(exec_ptr); builder.executable->name = "eval"_utf16_fly_string; auto result = builder.to_result(); // If the caller is strict, the eval is always strict regardless of what Rust reported. if (strict_caller == CallerMode::Strict) result.is_strict_mode = true; return result; } Optional>> compile_parsed_module(ParsedProgram* parsed, NonnullRefPtr source_code, Realm& realm) { if (!parsed) return {}; if (rust_parsed_program_has_errors(parsed)) { Vector parse_errors; rust_parsed_program_take_errors(parsed, &parse_errors, collect_parse_errors); rust_free_parsed_program(parsed); return parse_errors; } auto length = source_code->length_in_code_units(); GC::DeferGC defer_gc(realm.vm().heap()); ModuleBuilder builder; ModuleCallbacks callbacks { .set_has_top_level_await = module_set_has_top_level_await, .push_import_entry = module_push_import_entry, .push_local_export = module_push_local_export, .push_indirect_export = module_push_indirect_export, .push_star_export = module_push_star_export, .push_requested_module = module_push_requested_module, .set_default_export_binding = module_set_default_export_binding, .push_var_name = module_push_var_name, .push_function = module_push_function, .push_lexical_binding = module_push_lexical_binding, }; void* tla_executable = nullptr; void* exec_ptr = rust_compile_parsed_module(parsed, &realm.vm(), source_code.ptr(), &builder, &callbacks, &tla_executable, length); if (!exec_ptr && !tla_executable) return Vector {}; if (tla_executable) { auto& vm = realm.vm(); auto* tla_exec = static_cast(tla_executable); builder.result.tla_shared_data = vm.heap().allocate( vm, FunctionKind::Async, "module code with top-level await"_utf16_fly_string, 0, 0, true, false, true, Vector {}, nullptr); builder.result.tla_shared_data->m_is_module_wrapper = true; builder.result.tla_shared_data->m_uses_this = true; builder.result.tla_shared_data->m_function_environment_needed = true; builder.result.tla_shared_data->m_executable = tla_exec; } else { builder.result.executable = static_cast(exec_ptr); } return builder.result; } Optional>> compile_module(StringView source_text, Realm& realm, StringView filename) { auto source_code = SourceCode::create(String::from_utf8(filename).release_value_but_fixme_should_propagate_errors(), Utf16String::from_utf8(source_text)); auto const* source_ptr = source_code->utf16_data(); auto length = source_code->length_in_code_units(); auto* parsed = rust_parse_program(source_ptr, length, static_cast(ProgramType::Module), 0, g_dump_ast, g_dump_ast_use_color); return compile_parsed_module(parsed, source_code, realm); } Optional, String>> compile_dynamic_function( VM& vm, StringView source_text, StringView parameters_string, StringView body_parse_string, FunctionKind kind) { auto source_code = SourceCode::create({}, Utf16String::from_utf8(source_text)); auto const& code_view = source_code->code_view(); auto full_length = code_view.length_in_code_units(); auto params_utf16 = Utf16String::from_utf8(parameters_string); auto body_utf16 = Utf16String::from_utf8(body_parse_string); auto prepare_utf16 = [](Utf16View const& view, Vector& buf) -> u16 const* { if (view.has_ascii_storage()) { auto ascii = view.ascii_span(); buf.ensure_capacity(view.length_in_code_units()); for (size_t i = 0; i < view.length_in_code_units(); ++i) buf.unchecked_append(static_cast(ascii[i])); return buf.data(); } return reinterpret_cast(view.utf16_span().data()); }; Vector full_buf, params_buf, body_buf; auto const* full_data = prepare_utf16(code_view, full_buf); auto const* params_data = prepare_utf16(params_utf16.utf16_view(), params_buf); auto const* body_data = prepare_utf16(body_utf16.utf16_view(), body_buf); GC::DeferGC defer_gc(vm.heap()); String parse_error; void* sfd_ptr = rust_compile_dynamic_function( full_data, full_length, params_data, params_utf16.utf16_view().length_in_code_units(), body_data, body_utf16.utf16_view().length_in_code_units(), &vm, source_code.ptr(), static_cast(kind), &parse_error, collect_single_parse_error, nullptr, nullptr); if (!sfd_ptr) return parse_error; auto& function_data = *static_cast(sfd_ptr); function_data.m_source_text_owner = Utf16String::from_utf8(source_text); function_data.m_source_text = function_data.m_source_text_owner.utf16_view(); return GC::Ref { function_data }; } Optional>> compile_builtin_file( unsigned char const* script_text, VM& vm) { auto script_text_as_utf16 = Utf16String::from_utf8_without_validation({ script_text, strlen(reinterpret_cast(script_text)) }); auto code = SourceCode::create("BuiltinFile"_string, move(script_text_as_utf16)); auto const& code_view = code->code_view(); auto length = code_view.length_in_code_units(); GC::DeferGC defer_gc(vm.heap()); Vector> shared_data_list; auto const* source_ptr = code->utf16_data(); rust_compile_builtin_file(source_ptr, length, &vm, code.ptr(), &shared_data_list, collect_builtin_function, nullptr, nullptr); return shared_data_list; } GC::Ptr compile_function(VM& vm, SharedFunctionInstanceData& shared_data, bool builtin_abstract_operations_enabled) { if (!shared_data.m_use_rust_compilation) return nullptr; VERIFY(shared_data.m_rust_function_ast); GC::DeferGC defer_gc(vm.heap()); auto const* source_ptr = shared_data.m_source_code->utf16_data(); auto* exec = static_cast(rust_compile_function( &vm, shared_data.m_source_code.ptr(), source_ptr, shared_data.m_source_code->length_in_code_units(), &shared_data, shared_data.m_rust_function_ast, builtin_abstract_operations_enabled)); shared_data.m_rust_function_ast = nullptr; return exec; } void free_function_ast(void* ast) { if (ast) rust_free_function_ast(ast); } } // --- FFI factory functions (called by Rust to create C++ objects) --- namespace JS::FFI { struct RustCompiledRegex { String parsed_pattern; }; static Utf16View view_from_ffi(FFIUtf16Slice slice) { return JS::RustIntegration::utf16_view_from_bytes(slice.data, slice.length); } static Utf16String utf16_from_ffi(FFIUtf16Slice slice) { return Utf16String::from_utf16(view_from_ffi(slice)); } static Utf16FlyString utf16_fly_from_ffi(FFIUtf16Slice slice) { return Utf16FlyString::from_utf16(view_from_ffi(slice)); } static JS::Value decode_constant(JS::VM& vm, uint8_t const*& cursor, uint8_t const* end) { VERIFY(cursor < end); auto const tag = *cursor++; switch (static_cast(tag)) { case ConstantTag::Number: { VERIFY(cursor + 8 <= end); double value; memcpy(&value, cursor, 8); cursor += 8; return JS::Value(value); } case ConstantTag::BooleanTrue: return JS::Value(true); case ConstantTag::BooleanFalse: return JS::Value(false); case ConstantTag::Null: return JS::js_null(); case ConstantTag::Undefined: return JS::js_undefined(); case ConstantTag::Empty: return JS::js_special_empty_value(); case ConstantTag::String: { VERIFY(cursor + 4 <= end); uint32_t len; memcpy(&len, cursor, 4); cursor += 4; VERIFY(cursor + len * 2 <= end); if (len == 0) return JS::PrimitiveString::create(vm, Utf16String {}); // NB: cursor may not be 2-byte aligned, so copy to an aligned buffer. Vector aligned_buf; aligned_buf.resize(len); memcpy(aligned_buf.data(), cursor, len * 2); auto str = Utf16String::from_utf16(Utf16View(aligned_buf.data(), len)); cursor += len * 2; return JS::PrimitiveString::create(vm, move(str)); } case ConstantTag::BigInt: { VERIFY(cursor + 4 <= end); uint32_t len; memcpy(&len, cursor, 4); cursor += 4; VERIFY(cursor + len <= end); auto ascii = StringView(reinterpret_cast(cursor), len); cursor += len; auto integer = [&] { if (len >= 3 && ascii[0] == '0') { if (ascii[1] == 'x' || ascii[1] == 'X') return MUST(Crypto::SignedBigInteger::from_base(16, ascii.substring_view(2))); if (ascii[1] == 'o' || ascii[1] == 'O') return MUST(Crypto::SignedBigInteger::from_base(8, ascii.substring_view(2))); if (ascii[1] == 'b' || ascii[1] == 'B') return MUST(Crypto::SignedBigInteger::from_base(2, ascii.substring_view(2))); } return MUST(Crypto::SignedBigInteger::from_base(10, ascii)); }(); return JS::BigInt::create(vm, move(integer)); } case ConstantTag::RawValue: { VERIFY(cursor + 8 <= end); JS::Value value; memcpy(&value, cursor, 8); cursor += 8; return value; } default: VERIFY_NOT_REACHED(); } } extern "C" void* rust_create_executable( void* vm_ptr, void const* source_code_ptr, FFIExecutableData const* data) { auto& vm = *static_cast(vm_ptr); auto& source_code = *static_cast(source_code_ptr); // Build bytecode vector Vector bytecode_vec; bytecode_vec.append(data->bytecode, data->bytecode_length); // Build identifier table auto ident_table = make(); for (size_t i = 0; i < data->identifier_count; ++i) { ident_table->insert(utf16_fly_from_ffi(data->identifier_table[i])); } // Build property key table auto prop_key_table = make(); for (size_t i = 0; i < data->property_key_count; ++i) { prop_key_table->insert(utf16_fly_from_ffi(data->property_key_table[i])); } // Build string table auto str_table = make(); for (size_t i = 0; i < data->string_count; ++i) { str_table->insert(utf16_from_ffi(data->string_table[i])); } // Build regex table from pre-compiled regex objects. // NB: The regex table is no longer read at runtime (new_regexp uses pattern+flags directly), // but we still need to iterate and free the RustCompiledRegex objects. auto regex_tbl = make(); for (size_t i = 0; i < data->regex_count; ++i) { auto* cr = static_cast(data->compiled_regexes[i]); delete cr; } // Decode constants Vector constants_vec; constants_vec.ensure_capacity(data->constants_count); auto const* cursor = data->constants_data; auto const* end = data->constants_data + data->constants_data_length; for (size_t i = 0; i < data->constants_count; ++i) { constants_vec.append(decode_constant(vm, cursor, end)); } VERIFY(cursor == end); // Create executable auto executable = vm.heap().allocate( move(bytecode_vec), move(ident_table), move(prop_key_table), move(str_table), move(regex_tbl), move(constants_vec), source_code, data->property_lookup_cache_count, data->global_variable_cache_count, data->template_object_cache_count, data->object_shape_cache_count, data->object_property_iterator_cache_count, data->number_of_registers, data->is_strict ? JS::Strict::Yes : JS::Strict::No); // Set exception handlers for (size_t i = 0; i < data->exception_handler_count; ++i) { executable->exception_handlers.append({ data->exception_handlers[i].start_offset, data->exception_handlers[i].end_offset, data->exception_handlers[i].handler_offset, }); } // Set source map for (size_t i = 0; i < data->source_map_count; ++i) { executable->source_map.append({ data->source_map[i].bytecode_offset, { data->source_map[i].source_start, data->source_map[i].source_end }, }); } // Set basic block offsets for (size_t i = 0; i < data->basic_block_count; ++i) { executable->basic_block_start_offsets.append(data->basic_block_offsets[i]); } // Set local variable names for (size_t i = 0; i < data->local_variable_count; ++i) { executable->local_variable_names.append({ .name = utf16_fly_from_ffi(data->local_variable_names[i]), .declaration_kind = JS::LocalVariable::DeclarationKind::Var, }); } // Set layout indices executable->local_index_base = data->number_of_registers; executable->argument_index_base = data->number_of_registers + data->local_variable_count + data->constants_count; executable->registers_and_locals_count = data->number_of_registers + data->local_variable_count; executable->registers_and_locals_and_constants_count = data->number_of_registers + data->local_variable_count + data->constants_count; // Set length identifier (for GetLength optimization) if (data->length_identifier.has_value) executable->length_identifier = JS::Bytecode::PropertyKeyTableIndex(data->length_identifier.value); // Set shared function data (inner function definitions) for (size_t i = 0; i < data->shared_function_data_count; ++i) { auto* sfd = const_cast( static_cast(data->shared_function_data[i])); executable->shared_function_data.append(sfd); } // Set class blueprints (move from heap-allocated objects) for (size_t i = 0; i < data->class_blueprint_count; ++i) { auto* bp = static_cast(data->class_blueprints[i]); executable->class_blueprints.append(move(*bp)); delete bp; } executable->fixup_cache_pointers(); return executable.ptr(); } extern "C" void* rust_create_sfd( void* vm_ptr, void const* source_code_ptr, FFISharedFunctionData const* data) { auto& vm = *static_cast(vm_ptr); auto& source_code = *static_cast(source_code_ptr); auto fn_name = data->name_len > 0 ? Utf16FlyString::from_utf16(Utf16View(reinterpret_cast(data->name), data->name_len)) : Utf16FlyString {}; Vector mapped_param_names; if (data->has_simple_parameter_list) { mapped_param_names.ensure_capacity(data->parameter_name_count); for (size_t i = 0; i < data->parameter_name_count; ++i) mapped_param_names.append(utf16_fly_from_ffi(data->parameter_names[i])); } auto shared = vm.heap().allocate( vm, static_cast(data->function_kind), move(fn_name), data->function_length, data->formal_parameter_count, data->strict, data->is_arrow, data->has_simple_parameter_list, move(mapped_param_names), data->rust_function_ast); // Set parsing insights that must be available before lazy compilation. shared->m_uses_this = data->uses_this; if (data->uses_this_from_environment) shared->m_function_environment_needed = true; // Set source text as a view into the original source code. shared->m_source_code = &source_code; if (data->source_text_length > 0) { auto const& code_view = source_code.code_view(); shared->m_source_text = code_view.substring_view(data->source_text_offset, data->source_text_length); } return shared.ptr(); } extern "C" void rust_sfd_set_metadata( void* sfd_ptr, bool uses_this, bool function_environment_needed, size_t function_environment_bindings_count, bool might_need_arguments_object, bool contains_direct_call_to_eval) { auto& shared = *static_cast(sfd_ptr); shared.m_uses_this = uses_this; shared.m_function_environment_needed = function_environment_needed; shared.m_function_environment_bindings_count = function_environment_bindings_count; shared.m_might_need_arguments_object = might_need_arguments_object; shared.m_contains_direct_call_to_eval = contains_direct_call_to_eval; } extern "C" void rust_sfd_set_class_field_initializer_name( void* sfd_ptr, uint16_t const* name, size_t name_len, bool is_private) { auto& shared = *static_cast(sfd_ptr); auto utf16_name = Utf16FlyString::from_utf16(JS::RustIntegration::utf16_view_from_bytes(name, name_len)); if (is_private) { shared.m_class_field_initializer_name = JS::PrivateName(0, utf16_name); } else { shared.m_class_field_initializer_name = JS::PropertyKey(utf16_name.to_utf16_string()); } } extern "C" void* rust_create_class_blueprint( void* vm_ptr, void const* source_code_ptr, uint16_t const* name, size_t name_len, size_t source_text_offset, size_t source_text_len, uint32_t constructor_sfd_index, bool has_super_class, bool has_name, FFIClassElement const* elements, size_t element_count) { auto* blueprint = new JS::Bytecode::ClassBlueprint(); blueprint->constructor_shared_function_data_index = constructor_sfd_index; blueprint->has_super_class = has_super_class; blueprint->has_name = has_name; if (name_len > 0) blueprint->name = Utf16FlyString::from_utf16(JS::RustIntegration::utf16_view_from_bytes(name, name_len)); // Store source text as a view into the SourceCode buffer. if (source_text_len > 0) { auto& source_code = *static_cast(source_code_ptr); auto const& code_view = source_code.code_view(); blueprint->source_text = code_view.substring_view(source_text_offset, source_text_len); } for (size_t i = 0; i < element_count; ++i) { auto const& elem = elements[i]; JS::Bytecode::ClassElementDescriptor desc; desc.kind = static_cast(elem.kind); desc.is_static = elem.is_static; desc.is_private = elem.is_private; if (elem.private_identifier_len > 0) desc.private_identifier = Utf16FlyString::from_utf16(JS::RustIntegration::utf16_view_from_bytes(elem.private_identifier, elem.private_identifier_len)); if (elem.shared_function_data_index.has_value) desc.shared_function_data_index = elem.shared_function_data_index.value; desc.has_initializer = elem.has_initializer; switch (elem.literal_value_kind) { case LiteralValueKind::None: break; case LiteralValueKind::Number: desc.literal_value = JS::Value(elem.literal_value_number); break; case LiteralValueKind::BooleanTrue: desc.literal_value = JS::Value(true); break; case LiteralValueKind::BooleanFalse: desc.literal_value = JS::Value(false); break; case LiteralValueKind::Null: desc.literal_value = JS::js_null(); break; case LiteralValueKind::String: { auto& vm = *static_cast(vm_ptr); auto str_view = JS::RustIntegration::utf16_view_from_bytes(elem.literal_value_string, elem.literal_value_string_len); desc.literal_value = JS::Value(JS::PrimitiveString::create(vm, str_view)); break; } } blueprint->elements.append(desc); } return blueprint; } extern "C" void module_sfd_set_name( void* sfd_ptr, uint16_t const* name, size_t name_len) { auto& shared = *static_cast(sfd_ptr); shared.m_name = Utf16FlyString::from_utf16(JS::RustIntegration::utf16_view_from_bytes(name, name_len)); } extern "C" void* rust_compile_regex( uint16_t const* pattern_data, size_t pattern_len, uint16_t const* flags_data, size_t flags_len, char const** error_out) { *error_out = nullptr; auto pattern = JS::RustIntegration::utf16_view_from_bytes(pattern_data, pattern_len); auto flags_view = JS::RustIntegration::utf16_view_from_bytes(flags_data, flags_len); // Extract unicode/unicode_sets from flags for parse_regex_pattern. bool is_unicode = false; bool is_unicode_sets = false; for (size_t i = 0; i < flags_view.length_in_code_units(); ++i) { auto ch = flags_view.code_unit_at(i); if (ch == 'u') is_unicode = true; else if (ch == 'v') is_unicode_sets = true; } auto parsed_pattern = JS::parse_regex_pattern(pattern, is_unicode, is_unicode_sets); if (parsed_pattern.is_error()) { auto msg = MUST(String::formatted("RegExp compile error: {}", parsed_pattern.release_error().error)); auto* buf = static_cast(kmalloc(msg.byte_count() + 1)); memcpy(buf, msg.bytes().data(), msg.byte_count()); buf[msg.byte_count()] = '\0'; *error_out = buf; return nullptr; } auto pattern_str = parsed_pattern.release_value(); // Build compile flags from the flag characters. regex::ECMAScriptCompileFlags compile_flags {}; for (size_t i = 0; i < flags_view.length_in_code_units(); ++i) { auto ch = flags_view.code_unit_at(i); switch (ch) { case 'g': compile_flags.global = true; break; case 'i': compile_flags.ignore_case = true; break; case 'm': compile_flags.multiline = true; break; case 's': compile_flags.dot_all = true; break; case 'u': compile_flags.unicode = true; break; case 'v': compile_flags.unicode_sets = true; break; case 'y': compile_flags.sticky = true; break; case 'd': compile_flags.has_indices = true; break; default: break; } } auto compiled = regex::ECMAScriptRegex::compile(pattern_str.bytes_as_string_view(), compile_flags); if (compiled.is_error()) { auto msg = MUST(String::formatted("RegExp compile error: {}", compiled.release_error())); auto* buf = static_cast(kmalloc(msg.byte_count() + 1)); memcpy(buf, msg.bytes().data(), msg.byte_count()); buf[msg.byte_count()] = '\0'; *error_out = buf; return nullptr; } return new RustCompiledRegex { move(pattern_str) }; } extern "C" void rust_free_compiled_regex(void* ptr) { delete static_cast(ptr); } extern "C" void rust_free_error_string(char const* str) { kfree(const_cast(str)); } extern "C" size_t rust_number_to_utf16(double value, uint16_t* buffer, size_t buffer_len) { auto str = JS::number_to_utf16_string(value); auto view = str.utf16_view(); auto len = min(view.length_in_code_units(), buffer_len); for (size_t i = 0; i < len; ++i) buffer[i] = view.code_unit_at(i); return len; } // FIXME: This FFI workaround exists only to match C++ float-to-string // formatting in the Rust AST dump. Once the C++ pipeline is // removed, this can be deleted and the Rust side can use its own // formatting without needing to match C++. extern "C" size_t rust_format_double(double value, uint8_t* buffer, size_t buffer_len) { auto str = MUST(String::formatted("{}", value)); auto bytes = str.bytes(); auto len = min(bytes.size(), buffer_len); memcpy(buffer, bytes.data(), len); return len; } extern "C" uint64_t get_well_known_symbol(void* vm_ptr, WellKnownSymbolKind symbol_id) { auto& vm = *static_cast(vm_ptr); JS::Value value; switch (symbol_id) { case WellKnownSymbolKind::SymbolIterator: value = vm.well_known_symbol_iterator(); break; case WellKnownSymbolKind::SymbolAsyncIterator: value = vm.well_known_symbol_async_iterator(); break; default: VERIFY_NOT_REACHED(); } return value.encoded(); } extern "C" uint64_t get_abstract_operation_function(void* vm_ptr, uint16_t const* name, size_t name_len) { auto& vm = *static_cast(vm_ptr); auto& intrinsics = vm.current_realm()->intrinsics(); auto name_view = JS::RustIntegration::utf16_view_from_bytes(name, name_len); auto name_str = MUST(name_view.to_utf8()); #define __JS_ENUMERATE(snake_name, functionName, length) \ if (name_str == #functionName##sv) \ return JS::Value(intrinsics.snake_name##_abstract_operation_function().ptr()).encoded(); JS_ENUMERATE_NATIVE_JAVASCRIPT_BACKED_ABSTRACT_OPERATIONS #undef __JS_ENUMERATE VERIFY_NOT_REACHED(); } }