From 05c2f3b0b1e2239617c00e8951d3d9701fce556e Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Tue, 24 Jun 2025 05:59:21 -0700 Subject: [PATCH] [vm,dyn_modules] Hook interpreter to the standalone VM When dynamic modules are enabled, standalone VM can now run bytecode binaries directly. Also, if --interpreter flag is specified, Dart source is compiled to bytecode and interpreter is used to run it. This will allow us to test VM service capabilities including debugging and hot reload against the interpreter. TEST=manual Change-Id: Ibb5a67f4844485c4ed90b8a7568dc42fa552fcac Cq-Include-Trybots: luci.dart.try:vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436421 Reviewed-by: Ryan Macnak Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- pkg/dart2bytecode/bin/kernel_service.dart | 41 +++++++++++ pkg/vm/bin/kernel_service.dart | 68 +++++++++++++++++-- pkg/vm/test/kernel_service_test.dart | 19 +++--- runtime/bin/dart_api_win.c | 19 ++++++ runtime/bin/dartutils.cc | 6 +- runtime/bin/dartutils.h | 1 + runtime/bin/dfe.cc | 12 +++- runtime/bin/main_impl.cc | 9 ++- runtime/bin/snapshot_utils.h | 3 + runtime/include/dart_api.h | 24 +++++++ .../tests/vm/dart/exported_symbols_test.dart | 2 + runtime/vm/dart_api_impl.cc | 50 ++++++++++++++ runtime/vm/flag_list.h | 1 + runtime/vm/kernel_isolate.cc | 14 +++- utils/kernel-service/BUILD.gn | 18 +++-- 15 files changed, 257 insertions(+), 30 deletions(-) create mode 100644 pkg/dart2bytecode/bin/kernel_service.dart diff --git a/pkg/dart2bytecode/bin/kernel_service.dart b/pkg/dart2bytecode/bin/kernel_service.dart new file mode 100644 index 00000000000..a52c78e297a --- /dev/null +++ b/pkg/dart2bytecode/bin/kernel_service.dart @@ -0,0 +1,41 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:typed_data' show Uint8List; + +import 'package:dart2bytecode/bytecode_generator.dart' show generateBytecode; +import 'package:dart2bytecode/options.dart' show BytecodeOptions; +import 'package:kernel/ast.dart' show Component, Library; +import 'package:kernel/binary/ast_to_binary.dart' show BytesSink; +import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; +import 'package:kernel/core_types.dart' show CoreTypes; +import 'package:kernel/target/targets.dart' show Target; + +import '../../vm/bin/kernel_service.dart' as kernel_service; + +Uint8List _generateBytecode( + Component component, + List libraries, + CoreTypes coreTypes, + ClassHierarchy hierarchy, + Target target, + bool enableAsserts, +) { + final byteSink = new BytesSink(); + generateBytecode(component, byteSink, + libraries: libraries, + coreTypes: coreTypes, + hierarchy: hierarchy, + target: target, + options: BytecodeOptions( + enableAsserts: enableAsserts, emitSourcePositions: true)); + return byteSink.builder.takeBytes(); +} + +// Wire up bytecode generator to the kernel service to avoid +// circular dependency between package:vm and package:dart2bytecode. +main([args]) { + kernel_service.bytecodeGenerator = _generateBytecode; + return kernel_service.main(args); +} diff --git a/pkg/vm/bin/kernel_service.dart b/pkg/vm/bin/kernel_service.dart index 14c33e5d0b5..fd7751ede56 100644 --- a/pkg/vm/bin/kernel_service.dart +++ b/pkg/vm/bin/kernel_service.dart @@ -77,6 +77,18 @@ const int kRejectTag = 7; bool allowDartInternalImport = false; +// Bytecode generator, optionally injected in +// pkg/dart2bytecode/bin/kernel_service.dart. +Uint8List Function( + Component component, + List libraries, + CoreTypes coreTypes, + ClassHierarchy hierarchy, + Target target, + bool enableAsserts, +)? +bytecodeGenerator; + CompilerOptions setupCompilerOptions( FileSystem fileSystem, Uri? platformKernelPath, @@ -165,6 +177,7 @@ abstract class Compiler { final String invocationModes; final String verbosityLevel; final bool enableMirrors; + final bool generateBytecode; // Code coverage and hot reload are only supported by incremental compiler, // which is used if vm-service is enabled. @@ -189,6 +202,7 @@ abstract class Compiler { this.invocationModes = '', this.verbosityLevel = Verbosity.defaultValue, required this.enableMirrors, + required this.generateBytecode, }) { Uri? packagesUri = null; final packageConfig = this.packageConfig ?? Platform.packageConfig; @@ -309,6 +323,7 @@ class IncrementalCompilerWrapper extends Compiler { String invocationModes = '', String verbosityLevel = Verbosity.defaultValue, required bool enableMirrors, + required super.generateBytecode, }) : super( isolateGroupId, fileSystem, @@ -333,6 +348,7 @@ class IncrementalCompilerWrapper extends Compiler { String? packageConfig, String invocationModes = '', required bool enableMirrors, + required bool generateBytecode, }) { IncrementalCompilerWrapper result = IncrementalCompilerWrapper( isolateGroupId, @@ -343,6 +359,7 @@ class IncrementalCompilerWrapper extends Compiler { packageConfig: packageConfig, invocationModes: invocationModes, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); result.generator = new IncrementalCompiler.forExpressionCompilationOnly( component, @@ -381,6 +398,7 @@ class IncrementalCompilerWrapper extends Compiler { packageConfig: packageConfig, invocationModes: invocationModes, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); final generator = this.generator!; // TODO(VM TEAM): This does not seem safe. What if cloning while having @@ -424,6 +442,7 @@ class SingleShotCompilerWrapper extends Compiler { String invocationModes = '', String verbosityLevel = Verbosity.defaultValue, required bool enableMirrors, + required super.generateBytecode, }) : super( isolateGroupId, fileSystem, @@ -483,6 +502,7 @@ Future lookupOrBuildNewIncrementalCompiler( String invocationModes = '', String verbosityLevel = Verbosity.defaultValue, required bool enableMirrors, + required bool generateBytecode, }) async { IncrementalCompilerWrapper? compiler = lookupIncrementalCompiler( isolateGroupId, @@ -520,6 +540,7 @@ Future lookupOrBuildNewIncrementalCompiler( invocationModes: invocationModes, verbosityLevel: verbosityLevel, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); } isolateCompilers[isolateGroupId] = compiler; @@ -578,6 +599,7 @@ Future _processExpressionCompilationRequest(request) async { final List? experimentalFlags = request[19] != null ? request[19].cast() : null; final bool enableMirrors = request[20]; + final bool generateBytecode = request[21]; IncrementalCompilerWrapper? compiler = isolateCompilers[isolateGroupId]; @@ -676,6 +698,7 @@ Future _processExpressionCompilationRequest(request) async { experimentalFlags: experimentalFlags, packageConfig: packageConfigFile, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); isolateCompilers[isolateGroupId] = compiler; await compiler.compile( @@ -737,7 +760,20 @@ Future _processExpressionCompilationRequest(request) async { result = new CompilationResult.errors(compiler.errorsPlain); } else { Component component = createExpressionEvaluationComponent(procedure); - result = new CompilationResult.ok(serializeComponent(component)); + Uint8List bytes; + if (compiler.generateBytecode) { + bytes = bytecodeGenerator!.call( + component, + component.libraries, + compiler.generator!.lastKnownGoodResult!.coreTypes, + compiler.generator!.lastKnownGoodResult!.classHierarchy, + compiler.options.target!, + compiler.enableAsserts, + ); + } else { + bytes = serializeComponent(component); + } + result = new CompilationResult.ok(bytes); } } catch (error, stack) { result = new CompilationResult.crash(error, stack); @@ -867,6 +903,7 @@ Future _processLoadRequest(request) async { final String? multirootScheme = request[13]; final String verbosityLevel = request[14]; final bool enableMirrors = request[15]; + final bool generateBytecode = request[16]; Uri platformKernelPath; List? platformKernel = null; if (request[3] is String) { @@ -952,6 +989,7 @@ Future _processLoadRequest(request) async { invocationModes: invocationModes, verbosityLevel: verbosityLevel, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); fileSystem = compiler.fileSystem; } else { @@ -973,6 +1011,7 @@ Future _processLoadRequest(request) async { invocationModes: invocationModes, verbosityLevel: verbosityLevel, enableMirrors: enableMirrors, + generateBytecode: generateBytecode, ); } @@ -1028,13 +1067,30 @@ Future _processLoadRequest(request) async { // these sources built-in. Everything loaded as a summary in // [kernelForProgram] is marked `external`, so we can use that bit to // decide what to exclude. - result = new CompilationResult.ok( - serializeComponent( + Uint8List bytes; + if (compiler.generateBytecode) { + final generator = bytecodeGenerator; + if (generator == null) { + throw 'Cannot generate bytecode as dynamic modules are disabled.'; + } + bytes = generator( + compilerResult.component!, + compilerResult.component!.libraries + .where((lib) => !loadedLibraries.contains(lib)) + .toList(), + compilerResult.coreTypes!, + compilerResult.classHierarchy!, + compiler.options.target!, + compiler.enableAsserts, + ); + } else { + bytes = serializeComponent( compilerResult.component!, filter: (lib) => !loadedLibraries.contains(lib), nativeAssetsComponent: nativeAssetsComponent, - ), - ); + ); + } + result = new CompilationResult.ok(bytes); } } catch (error, stack) { result = new CompilationResult.crash(error, stack); @@ -1232,7 +1288,7 @@ Future trainInternal(String scriptUri, String? platformKernelPath) async { null /* multirootScheme */, 'all' /* CFE logging mode */, true /* enableMirrors */, - null /* native assets yaml */, + false /* generateBytecode */, ]; await _processLoadRequest(request); } diff --git a/pkg/vm/test/kernel_service_test.dart b/pkg/vm/test/kernel_service_test.dart index e3b7a806481..4c7b7d3906b 100644 --- a/pkg/vm/test/kernel_service_test.dart +++ b/pkg/vm/test/kernel_service_test.dart @@ -139,15 +139,16 @@ Future singleShotCompile( /* [4] = bool = incremental = */ false, /* [5] = bool = for_snapshot = */ false, /* [6] = bool = embed_sources = */ true, - /* [8] = int = isolateGroupId = */ 42, - /* [9] = List = sourceFiles = */ sourceFiles, - /* [10] = bool = enableAsserts = */ true, - /* [11] = List? = experimentalFlags = */ [], - /* [12] = String? = packageConfig = */ packageConfig, - /* [13] = String? = multirootFilepaths = */ null, - /* [14] = String? = multirootScheme = */ null, - /* [16] = String = verbosityLevel = */ Verbosity.all.name, - /* [17] = bool = enableMirrors = */ false, + /* [7] = int = isolateGroupId = */ 42, + /* [8] = List = sourceFiles = */ sourceFiles, + /* [9] = bool = enableAsserts = */ true, + /* [10] = List? = experimentalFlags = */ [], + /* [11] = String? = packageConfig = */ packageConfig, + /* [12] = String? = multirootFilepaths = */ null, + /* [13] = String? = multirootScheme = */ null, + /* [14] = String = verbosityLevel = */ Verbosity.all.name, + /* [15] = bool = enableMirrors = */ false, + /* [16] = bool = generateBytecode = */ false, ]); // Wait for kernel-service response. diff --git a/runtime/bin/dart_api_win.c b/runtime/bin/dart_api_win.c index 1eb08ab7c9f..44f7f42b42c 100644 --- a/runtime/bin/dart_api_win.c +++ b/runtime/bin/dart_api_win.c @@ -111,6 +111,7 @@ typedef void (*Dart_ExitIsolateType)(); typedef Dart_Handle ( *Dart_CreateSnapshotType)(uint8_t**, intptr_t*, uint8_t**, intptr_t*, bool); typedef bool (*Dart_IsKernelType)(const uint8_t*, intptr_t); +typedef bool (*Dart_IsBytecodeType)(const uint8_t*, intptr_t); typedef char* (*Dart_IsolateMakeRunnableType)(Dart_Isolate); typedef void (*Dart_SetMessageNotifyCallbackType)(Dart_MessageNotifyCallback); typedef Dart_MessageNotifyCallback (*Dart_GetMessageNotifyCallbackType)(); @@ -347,6 +348,8 @@ typedef Dart_Handle (*Dart_DeferredLoadCompleteErrorType)(intptr_t, const char*, bool); typedef Dart_Handle (*Dart_LoadScriptFromKernelType)(const uint8_t*, intptr_t); +typedef Dart_Handle (*Dart_LoadScriptFromBytecodeType)(const uint8_t*, + intptr_t); typedef Dart_Handle (*Dart_RootLibraryType)(); typedef Dart_Handle (*Dart_SetRootLibraryType)(Dart_Handle); typedef Dart_Handle (*Dart_GetTypeType)(Dart_Handle, @@ -528,6 +531,7 @@ static Dart_AddSymbolsType Dart_AddSymbolsFn = NULL; static Dart_ExitIsolateType Dart_ExitIsolateFn = NULL; static Dart_CreateSnapshotType Dart_CreateSnapshotFn = NULL; static Dart_IsKernelType Dart_IsKernelFn = NULL; +static Dart_IsBytecodeType Dart_IsBytecodeFn = NULL; static Dart_IsolateMakeRunnableType Dart_IsolateMakeRunnableFn = NULL; static Dart_SetMessageNotifyCallbackType Dart_SetMessageNotifyCallbackFn = NULL; static Dart_GetMessageNotifyCallbackType Dart_GetMessageNotifyCallbackFn = NULL; @@ -692,6 +696,7 @@ static Dart_DeferredLoadCompleteType Dart_DeferredLoadCompleteFn = NULL; static Dart_DeferredLoadCompleteErrorType Dart_DeferredLoadCompleteErrorFn = NULL; static Dart_LoadScriptFromKernelType Dart_LoadScriptFromKernelFn = NULL; +static Dart_LoadScriptFromBytecodeType Dart_LoadScriptFromBytecodeFn = NULL; static Dart_RootLibraryType Dart_RootLibraryFn = NULL; static Dart_SetRootLibraryType Dart_SetRootLibraryFn = NULL; static Dart_GetTypeType Dart_GetTypeFn = NULL; @@ -898,6 +903,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { (Dart_CreateSnapshotType)GetProcAddress(process, "Dart_CreateSnapshot"); Dart_IsKernelFn = (Dart_IsKernelType)GetProcAddress(process, "Dart_IsKernel"); + Dart_IsBytecodeFn = + (Dart_IsBytecodeType)GetProcAddress(process, "Dart_IsBytecode"); Dart_IsolateMakeRunnableFn = (Dart_IsolateMakeRunnableType)GetProcAddress( process, "Dart_IsolateMakeRunnable"); Dart_SetMessageNotifyCallbackFn = @@ -1225,6 +1232,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { process, "Dart_DeferredLoadCompleteError"); Dart_LoadScriptFromKernelFn = (Dart_LoadScriptFromKernelType)GetProcAddress( process, "Dart_LoadScriptFromKernel"); + Dart_LoadScriptFromBytecodeFn = + (Dart_LoadScriptFromBytecodeType)GetProcAddress( + process, "Dart_LoadScriptFromBytecode"); Dart_RootLibraryFn = (Dart_RootLibraryType)GetProcAddress(process, "Dart_RootLibrary"); Dart_SetRootLibraryFn = @@ -1648,6 +1658,10 @@ bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size) { return Dart_IsKernelFn(buffer, buffer_size); } +bool Dart_IsBytecode(const uint8_t* buffer, intptr_t buffer_size) { + return Dart_IsBytecodeFn(buffer, buffer_size); +} + char* Dart_IsolateMakeRunnable(Dart_Isolate isolate) { return Dart_IsolateMakeRunnableFn(isolate); } @@ -2369,6 +2383,11 @@ Dart_Handle Dart_LoadScriptFromKernel(const uint8_t* kernel_buffer, return Dart_LoadScriptFromKernelFn(kernel_buffer, kernel_size); } +Dart_Handle Dart_LoadScriptFromBytecode(const uint8_t* kernel_buffer, + intptr_t kernel_size) { + return Dart_LoadScriptFromBytecodeFn(kernel_buffer, kernel_size); +} + Dart_Handle Dart_RootLibrary() { return Dart_RootLibraryFn(); } diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc index 6adb3c3249a..88cd3721f88 100644 --- a/runtime/bin/dartutils.cc +++ b/runtime/bin/dartutils.cc @@ -44,6 +44,7 @@ MagicNumberData kernel_magic_number = {4, {0x90, 0xab, 0xcd, 0xef}}; MagicNumberData kernel_list_magic_number = { 7, {0x23, 0x40, 0x64, 0x69, 0x6c, 0x6c, 0x0a}}; // #@dill\n +MagicNumberData bytecode_magic_number = {4, {0x33, 0x43, 0x42, 0x44}}; MagicNumberData gzip_magic_number = {2, {0x1f, 0x8b, 0, 0}}; static bool IsWindowsHost() { @@ -398,6 +399,7 @@ DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const char* filename) { ASSERT(aotcoff_riscv64_magic_number.length <= appjit_magic_number.length); ASSERT(kernel_magic_number.length <= appjit_magic_number.length); ASSERT(kernel_list_magic_number.length <= appjit_magic_number.length); + ASSERT(bytecode_magic_number.length <= appjit_magic_number.length); ASSERT(gzip_magic_number.length <= appjit_magic_number.length); if (File::GetType(nullptr, filename, true) == File::kIsFile) { File* file = File::Open(nullptr, filename, File::kRead); @@ -426,8 +428,8 @@ DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const uint8_t* buffer, return kKernelListMagicNumber; } - if (CheckMagicNumber(buffer, buffer_length, gzip_magic_number)) { - return kGzipMagicNumber; + if (CheckMagicNumber(buffer, buffer_length, bytecode_magic_number)) { + return kBytecodeMagicNumber; } if (CheckMagicNumber(buffer, buffer_length, gzip_magic_number)) { diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h index 365666ea9d0..e96dcdc92df 100644 --- a/runtime/bin/dartutils.h +++ b/runtime/bin/dartutils.h @@ -256,6 +256,7 @@ class DartUtils { kAppJITMagicNumber, kKernelMagicNumber, kKernelListMagicNumber, + kBytecodeMagicNumber, kGzipMagicNumber, kAotELFMagicNumber, // Only the host-endian magic numbers are recognized, not the reverse-endian diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc index 8a10d19ce01..ee45c40a0d1 100644 --- a/runtime/bin/dfe.cc +++ b/runtime/bin/dfe.cc @@ -238,7 +238,8 @@ void DFE::ReadScript(const char* script_uri, kernel_buffer_size, decode_uri)) { return; } - if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size)) { + if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size) && + !Dart_IsBytecode(*kernel_buffer, *kernel_buffer_size)) { free(*kernel_buffer); *kernel_buffer = nullptr; *kernel_buffer_size = -1; @@ -259,7 +260,8 @@ static bool TryReadSimpleKernelBuffer(uint8_t* buffer, intptr_t* p_kernel_ir_size) { DartUtils::MagicNumber magic_number = DartUtils::SniffForMagicNumber(buffer, *p_kernel_ir_size); - if (magic_number == DartUtils::kKernelMagicNumber) { + if ((magic_number == DartUtils::kKernelMagicNumber) || + (magic_number == DartUtils::kBytecodeMagicNumber)) { // Do not free buffer if this is a kernel file - kernel_file will be // backed by the same memory as the buffer and caller will own it. // Caller is responsible for freeing the buffer when this function @@ -428,7 +430,7 @@ bool DFE::TryReadKernelFile(const char* script_uri, *kernel_ir_size = -1; if (app_snapshot == nullptr || app_snapshot->IsKernel() || - app_snapshot->IsKernelList()) { + app_snapshot->IsKernelList() || app_snapshot->IsBytecode()) { uint8_t* buffer; if (!TryReadFile(script_uri, &buffer, kernel_ir_size, decode_uri)) { return false; @@ -440,6 +442,10 @@ bool DFE::TryReadKernelFile(const char* script_uri, magic_number = DartUtils::kKernelMagicNumber; ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == DartUtils::kKernelMagicNumber); + } else if (app_snapshot->IsBytecode()) { + magic_number = DartUtils::kBytecodeMagicNumber; + ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == + DartUtils::kBytecodeMagicNumber); } else { magic_number = DartUtils::kKernelListMagicNumber; ASSERT(DartUtils::SniffForMagicNumber(buffer, *kernel_ir_size) == diff --git a/runtime/bin/main_impl.cc b/runtime/bin/main_impl.cc index 819d7de32b9..230ae3fb19a 100644 --- a/runtime/bin/main_impl.cc +++ b/runtime/bin/main_impl.cc @@ -333,8 +333,13 @@ static Dart_Isolate IsolateSetupHelper(Dart_Isolate isolate, CHECK_RESULT(uri); Dart_Handle resolved_script_uri = DartUtils::ResolveScript(uri); CHECK_RESULT(resolved_script_uri); - result = Dart_LoadScriptFromKernel(kernel_buffer, kernel_buffer_size); - CHECK_RESULT(result); + if (Dart_IsBytecode(kernel_buffer, kernel_buffer_size)) { + result = Dart_LoadScriptFromBytecode(kernel_buffer, kernel_buffer_size); + CHECK_RESULT(result); + } else { + result = Dart_LoadScriptFromKernel(kernel_buffer, kernel_buffer_size); + CHECK_RESULT(result); + } } #endif // !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/bin/snapshot_utils.h b/runtime/bin/snapshot_utils.h index 8ede183411e..3103d123405 100644 --- a/runtime/bin/snapshot_utils.h +++ b/runtime/bin/snapshot_utils.h @@ -29,6 +29,9 @@ class AppSnapshot { bool IsKernelList() const { return magic_number_ == DartUtils::kKernelListMagicNumber; } + bool IsBytecode() const { + return magic_number_ == DartUtils::kBytecodeMagicNumber; + } protected: explicit AppSnapshot(DartUtils::MagicNumber num) : magic_number_(num) {} diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 909d1c61b8a..34734347732 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -1460,6 +1460,16 @@ Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer, */ DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size); +/** + * Returns whether the buffer contains a bytecode file. + * + * \param buffer Pointer to a buffer that might contain a bytecode binary. + * \param buffer_size Size of the buffer. + * + * \return Whether the buffer contains a bytecode binary. + */ +DART_EXPORT bool Dart_IsBytecode(const uint8_t* buffer, intptr_t buffer_size); + /** * Make isolate runnable. * @@ -3551,6 +3561,20 @@ Dart_DeferredLoadCompleteError(intptr_t loading_unit_id, DART_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle Dart_LoadScriptFromKernel(const uint8_t* kernel_buffer, intptr_t kernel_size); +/** + * Loads the root library for the current isolate. + * + * Requires there to be no current root library. + * + * \param kernel_buffer A buffer which contains a bytecode binary. + * Must remain valid until isolate group shutdown. + * \param kernel_size Length of the passed in buffer. + * + * \return A handle to the root library, or an error. + */ +DART_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle +Dart_LoadScriptFromBytecode(const uint8_t* kernel_buffer, intptr_t kernel_size); + /** * Gets the library for the root script for the current isolate. * diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart index 508901dd758..e0e39547ebd 100644 --- a/runtime/tests/vm/dart/exported_symbols_test.dart +++ b/runtime/tests/vm/dart/exported_symbols_test.dart @@ -167,6 +167,7 @@ main() { "Dart_IsApiError", "Dart_IsBoolean", "Dart_IsByteBuffer", + "Dart_IsBytecode", "Dart_IsClosure", "Dart_IsCompilationError", "Dart_IsDouble", @@ -228,6 +229,7 @@ main() { "Dart_LoadingUnitLibraryUris", "Dart_LoadLibrary", "Dart_LoadLibraryFromKernel", + "Dart_LoadScriptFromBytecode", "Dart_LoadScriptFromKernel", "Dart_LookupLibrary", "Dart_MapContainsKey", diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 957b38a5733..219f4334e74 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -13,6 +13,7 @@ #include "platform/assert.h" #include "platform/unicode.h" #include "vm/app_snapshot.h" +#include "vm/bytecode_reader.h" #include "vm/class_finalizer.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart.h" @@ -1939,6 +1940,14 @@ DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size) { (buffer[3] == 0xef); } +DART_EXPORT bool Dart_IsBytecode(const uint8_t* buffer, intptr_t buffer_size) { + if (buffer_size < 4) { + return false; + } + return (buffer[0] == 0x33) && (buffer[1] == 0x43) && (buffer[2] == 0x42) && + (buffer[3] == 0x44); +} + DART_EXPORT char* Dart_IsolateMakeRunnable(Dart_Isolate isolate) { CHECK_NO_ISOLATE(Isolate::Current()); API_TIMELINE_DURATION(Thread::Current()); @@ -5473,6 +5482,47 @@ DART_EXPORT Dart_Handle Dart_LoadScriptFromKernel(const uint8_t* buffer, #endif // defined(DART_PRECOMPILED_RUNTIME) } +DART_EXPORT Dart_Handle Dart_LoadScriptFromBytecode(const uint8_t* buffer, + intptr_t buffer_size) { +#if defined(DART_DYNAMIC_MODULES) + DARTSCOPE(Thread::Current()); + API_TIMELINE_DURATION(T); + StackZone zone(T); + IsolateGroup* IG = T->isolate_group(); + + Library& library = Library::Handle(Z, IG->object_store()->root_library()); + if (!library.IsNull()) { + const String& library_url = String::Handle(Z, library.url()); + return Api::NewError("%s: A script has already been loaded from '%s'.", + CURRENT_FUNC, library_url.ToCString()); + } + CHECK_CALLBACK_STATE(T); + + // NOTE: We do not attach a finalizer for this object, because the embedder + // will free it once the isolate group has shutdown. + const auto& typed_data = ExternalTypedData::Handle(ExternalTypedData::New( + kExternalTypedDataUint8ArrayCid, const_cast(buffer), + buffer_size, Heap::kOld)); + + SafepointWriteRwLocker ml(T, IG->program_lock()); + bytecode::BytecodeLoader loader(T, typed_data); + const Function& function = Function::Handle(loader.LoadBytecode()); + + if (function.IsNull()) { + return Api::NewError( + "Invoked Dart programs must have a 'main' function defined:\n" + "https://dart.dev/to/main-function"); + } + library ^= Class::Handle(function.Owner()).library(); + IG->object_store()->set_root_library(library); + return Api::NewHandle(T, library.ptr()); +#else + return Api::NewError( + "%s: Cannot load bytecode as dynamic modules are disabled.", + CURRENT_FUNC); +#endif // defined(DART_DYNAMIC_MODULES) +} + DART_EXPORT Dart_Handle Dart_RootLibrary() { Thread* thread = Thread::Current(); IsolateGroup* isolate_group = thread->isolate_group(); diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h index b90b3e1ed27..eb7634ef520 100644 --- a/runtime/vm/flag_list.h +++ b/runtime/vm/flag_list.h @@ -123,6 +123,7 @@ constexpr bool FLAG_support_il_printer = false; P(idle_duration_micros, int, kMaxInt32, \ "Allow idle tasks to run for this long.") \ P(interpret_irregexp, bool, false, "Use irregexp bytecode interpreter") \ + C(interpreter, false, false, bool, false, "Use bytecode interpreter") \ P(link_natives_lazily, bool, false, "Link native calls lazily") \ R(log_marker_tasks, false, bool, false, \ "Log debugging information for old gen GC marking tasks.") \ diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index eda73cc8985..c89cef001d2 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -685,6 +685,10 @@ class KernelCompilationRequest : public ValueObject { enable_mirrors.type = Dart_CObject_kBool; enable_mirrors.value.as_bool = FLAG_enable_mirrors; + Dart_CObject generate_bytecode; + generate_bytecode.type = Dart_CObject_kBool; + generate_bytecode.value.as_bool = FLAG_interpreter; + Dart_CObject message; message.type = Dart_CObject_kArray; Dart_CObject* message_arr[] = {&tag, @@ -707,7 +711,8 @@ class KernelCompilationRequest : public ValueObject { &num_blob_loads, &enable_asserts, &experimental_flags_object, - &enable_mirrors}; + &enable_mirrors, + &generate_bytecode}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); @@ -914,6 +919,10 @@ class KernelCompilationRequest : public ValueObject { enable_mirrors.type = Dart_CObject_kBool; enable_mirrors.value.as_bool = FLAG_enable_mirrors; + Dart_CObject generate_bytecode; + generate_bytecode.type = Dart_CObject_kBool; + generate_bytecode.value.as_bool = FLAG_interpreter; + Dart_CObject* message_arr[] = {&tag, &send_port, &uri, @@ -929,7 +938,8 @@ class KernelCompilationRequest : public ValueObject { &multiroot_filepaths_object, &multiroot_scheme_object, &verbosity_str, - &enable_mirrors}; + &enable_mirrors, + &generate_bytecode}; message.value.as_array.values = message_arr; message.value.as_array.length = ARRAY_SIZE(message_arr); // Send the message. diff --git a/utils/kernel-service/BUILD.gn b/utils/kernel-service/BUILD.gn index 34b7b85e18b..89069fc1061 100644 --- a/utils/kernel-service/BUILD.gn +++ b/utils/kernel-service/BUILD.gn @@ -11,6 +11,12 @@ import("../create_timestamp.gni") _dart_root = get_path_info("../..", "abspath") +if (dart_dynamic_modules) { + _kernel_service_script = "pkg/dart2bytecode/bin/kernel_service.dart" +} else { + _kernel_service_script = "pkg/vm/bin/kernel_service.dart" +} + group("kernel-service") { if (dart_snapshot_kind == "app-jit") { deps = [ ":copy_kernel-service_snapshot" ] @@ -20,14 +26,14 @@ group("kernel-service") { } application_snapshot("kernel-service_snapshot") { - main_dart = "../../pkg/vm/bin/kernel_service.dart" + main_dart = "../../$_kernel_service_script" training_args = [ "--train", # Force triple-slashes both on Windows and otherwise. # Becomes e.g. file:///full/path/to/file and "file:///C:/full/path/to/file. # Without the ', "/"' part, on Linux it would get four slashes. - "file:///" + rebase_path("../../pkg/vm/bin/kernel_service.dart", "/"), + "file:///" + rebase_path("../../$_kernel_service_script", "/"), ] output = "$root_gen_dir/kernel-service.dart.snapshot" } @@ -93,7 +99,7 @@ template("kernel_service_dill") { } gen_kernel_tool = "//utils:gen_kernel.exe($host_toolchain)" - kernel_service_script = "../../pkg/vm/bin/kernel_service.dart" + kernel_service_script = "../../$_kernel_service_script" deps = [ "../../runtime/vm:vm_platform", @@ -131,7 +137,7 @@ template("kernel_service_dill") { "--no-aot", "--no-embed-sources", "--output=" + rebase_path(output, root_build_dir), - scheme + ":///pkg/vm/bin/kernel_service.dart", + scheme + ":///$_kernel_service_script", ] } } else { @@ -140,7 +146,7 @@ template("kernel_service_dill") { "../../runtime/vm:kernel_platform_files($host_toolchain)", "../../runtime/vm:vm_platform", ] - kernel_service_script = "../../pkg/vm/bin/kernel_service.dart" + kernel_service_script = "../../$_kernel_service_script" gen_kernel_script = "../../pkg/vm/bin/gen_kernel.dart" inputs = [ @@ -179,7 +185,7 @@ template("kernel_service_dill") { "--no-embed-sources", "--output=" + rebase_path(output, root_build_dir), ] - args += [ scheme + ":///pkg/vm/bin/kernel_service.dart" ] + args += [ scheme + ":///$_kernel_service_script" ] } } }