| // Copyright (c) 2024, 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 'package:dwds/src/loaders/strategy.dart'; |
| |
| class DartRuntimeDebugger { |
| final LoadStrategy _loadStrategy; |
| final bool _useLibraryBundleExpression; |
| |
| DartRuntimeDebugger({ |
| required LoadStrategy loadStrategy, |
| required bool useLibraryBundleExpression, |
| }) : _loadStrategy = loadStrategy, |
| _useLibraryBundleExpression = useLibraryBundleExpression; |
| |
| /// Generates a JS expression based on DDC module format. |
| String _generateJsExpression( |
| String ddcExpression, |
| String libraryBundleExpression, |
| ) => _useLibraryBundleExpression ? libraryBundleExpression : ddcExpression; |
| |
| /// Wraps a JS function call with SDK loader logic. |
| String _wrapWithSdkLoader(String args, String functionCall) { |
| return ''' |
| function($args) { |
| const sdk = ${_loadStrategy.loadModuleSnippet}('dart_sdk'); |
| const dart = sdk.dart; |
| return dart.$functionCall; |
| } |
| '''; |
| } |
| |
| /// Wraps a JS function call with DDC library bundle loader logic. |
| String _wrapWithBundleLoader(String args, String functionCall) { |
| return ''' |
| function($args) { |
| return dartDevEmbedder.debugger.$functionCall; |
| } |
| '''; |
| } |
| |
| /// Wraps an expression in an Immediately Invoked Function Expression (IIFE). |
| String _wrapInIIFE(String expression) { |
| return '($expression)()'; |
| } |
| |
| /// Builds a JS expression based on the loading strategy. |
| String _buildExpression( |
| String args, |
| String ddcFunction, |
| String libraryBundleFunction, |
| ) { |
| return _generateJsExpression( |
| _wrapWithSdkLoader(args, ddcFunction), |
| _wrapWithBundleLoader(args, libraryBundleFunction), |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving object metadata. |
| String getObjectMetadataJsExpression() { |
| return _buildExpression( |
| 'arg', |
| 'getObjectMetadata(arg)', |
| 'getObjectMetadata(arg)', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving object field names. |
| String getObjectFieldNamesJsExpression() { |
| return _buildExpression( |
| '', |
| 'getObjectFieldNames(this)', |
| 'getObjectFieldNames(this)', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving function metadata. |
| String getFunctionMetadataJsExpression() { |
| return _buildExpression( |
| '', |
| 'getFunctionMetadata(this)', |
| 'getFunctionName(this)', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving a subrange of elements. |
| String getSubRangeJsExpression() { |
| return _buildExpression( |
| 'offset, count', |
| 'getSubRange(this, offset, count)', |
| 'getSubRange(this, offset, count)', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving class metadata. |
| String getClassMetadataJsExpression(String libraryUri, String className) { |
| final expression = _buildExpression( |
| '', |
| "getClassMetadata('$libraryUri', '$className')", |
| "getClassMetadata('$libraryUri', '$className')", |
| ); |
| // Use the helper method to wrap this in an IIFE |
| return _wrapInIIFE(expression); |
| } |
| |
| /// Generates a JS expression for retrieving Dart Developer Extension Names. |
| String getDartDeveloperExtensionNamesJsExpression() { |
| return _generateJsExpression( |
| "${_loadStrategy.loadModuleSnippet}('dart_sdk').developer._extensions.keys.toList();", |
| 'dartDevEmbedder.debugger.extensionNames', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving metadata of classes in a library. |
| String getClassesInLibraryJsExpression(String libraryUri) { |
| final expression = _buildExpression( |
| '', |
| "getLibraryMetadata('$libraryUri')", |
| "getClassesInLibrary('$libraryUri')", |
| ); |
| // Use the helper method to wrap this in an IIFE |
| return _wrapInIIFE(expression); |
| } |
| |
| /// Generates a JS expression for retrieving map elements. |
| String getMapElementsJsExpression() { |
| return _buildExpression('', 'getMapElements(this)', 'getMapElements(this)'); |
| } |
| |
| /// Generates a JS expression for getting a property from a JS object. |
| String getPropertyJsExpression(String fieldName) { |
| return _generateJsExpression( |
| ''' |
| function() { |
| return this["$fieldName"]; |
| } |
| ''', |
| ''' |
| function() { |
| return this["$fieldName"]; |
| } |
| ''', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving set elements. |
| String getSetElementsJsExpression() { |
| return _buildExpression('', 'getSetElements(this)', 'getSetElements(this)'); |
| } |
| |
| /// Generates a JS expression for retrieving the fields of a record. |
| String getRecordFieldsJsExpression() { |
| return _buildExpression( |
| '', |
| 'getRecordFields(this)', |
| 'getRecordFields(this)', |
| ); |
| } |
| |
| /// Generates a JS expression for retrieving the fields of a record type. |
| String getRecordTypeFieldsJsExpression() { |
| return _buildExpression( |
| '', |
| 'getRecordTypeFields(this)', |
| 'getRecordTypeFields(this)', |
| ); |
| } |
| |
| /// Generates a JS expression for calling an instance method on an object. |
| String callInstanceMethodJsExpression(String methodName) { |
| String generateInstanceMethodJsExpression(String functionCall) { |
| return ''' |
| function () { |
| if (!Object.getPrototypeOf(this)) { return 'Instance of PlainJavaScriptObject'; } |
| return $functionCall; |
| } |
| '''; |
| } |
| |
| return _generateJsExpression( |
| generateInstanceMethodJsExpression( |
| '${_loadStrategy.loadModuleSnippet}("dart_sdk").dart.dsendRepl(this, "$methodName", arguments)', |
| ), |
| generateInstanceMethodJsExpression( |
| 'dartDevEmbedder.debugger.callInstanceMethod(this, "$methodName", arguments)', |
| ), |
| ); |
| } |
| |
| /// Generates a JS expression to invoke a Dart extension method. |
| String invokeExtensionJsExpression(String methodName, String encodedJson) { |
| return _generateJsExpression( |
| "${_loadStrategy.loadModuleSnippet}('dart_sdk').developer.invokeExtension('$methodName', JSON.stringify($encodedJson));", |
| "dartDevEmbedder.debugger.invokeExtension('$methodName', JSON.stringify($encodedJson));", |
| ); |
| } |
| |
| /// Generates a JS expression for calling a library method. |
| String callLibraryMethodJsExpression(String libraryUri, String methodName) { |
| final findLibraryExpression = |
| ''' |
| (function() { |
| const sdk = ${_loadStrategy.loadModuleSnippet}('dart_sdk'); |
| const dart = sdk.dart; |
| const library = dart.getLibrary('$libraryUri'); |
| if (!library) throw 'cannot find library for $libraryUri'; |
| return library; |
| })(); |
| '''; |
| |
| // `callLibraryMethod` expects an array of arguments. Chrome DevTools spreads |
| // arguments individually when calling functions. This code reconstructs the |
| // expected argument array. |
| return _generateJsExpression( |
| findLibraryExpression, |
| _wrapWithBundleLoader( |
| '', |
| 'callLibraryMethod("$libraryUri", "$methodName", Array.from(arguments))', |
| ), |
| ); |
| } |
| } |