blob: 67feb7ad29874a574ffb9034ce49ec86232f26fe [file] [log] [blame]
// Copyright (c) 2019, 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/utilities/shared.dart';
import '../utilities/objects.dart';
import 'debugger.dart';
/// Find the visible Dart properties from a JS Scope Chain, coming from the
/// scopeChain attribute of a Chrome CallFrame corresponding to [callFrameId].
///
/// See
/// https://chromedevtools.github.io/devtools-protocol/tot/Debugger#type-CallFrame
///
/// The [scopeList] is a List of Maps corresponding to Chrome Scope objects.
/// https://chromedevtools.github.io/devtools-protocol/tot/Debugger#type-Scope
Future<List<Property>> visibleProperties(
{List<Map<String, dynamic>> scopeList,
Debugger debugger,
String callFrameId}) async {
// We skip the global and the outer library scope and assume everything before
// that is a method scope.
var numberOfMethods = scopeList.length - 2;
if (numberOfMethods <= 0) return [];
var methodScopes = scopeList.sublist(0, numberOfMethods);
var propertyLists = methodScopes
.map((scope) async =>
await debugger.getProperties(scope['object']['objectId'] as String))
.toList();
var allProperties = [for (var list in propertyLists) ...await list];
var existingThis =
allProperties.firstWhere((x) => x.name == 'this', orElse: () => null);
if (existingThis == null) {
var syntheticThis = await _findMissingThis(callFrameId, debugger);
if (syntheticThis != null) {
allProperties.add(syntheticThis);
}
}
return allProperties;
}
/// Find the `this` in scope if it wasn't in the provided data from Chrome.
///
/// If we were not given a `this` value in the Chrome scopes that might mean
/// we're in a nested closure, or we might be a top-level function. Find it by evaluating
/// code in the JS frame. If it's null/undefined or is a Dart library scope, then
/// return null. Otherwise make a property for `this` and return it.
Future<Property> _findMissingThis(String callFrameId, Debugger debugger) async {
// If 'this' is a library return null, otherwise
// return 'this'.
final findCurrent = '''
(function (THIS) {
if (THIS === window) { return null; }
let dart = $loadModule('dart_sdk').dart
let libs = dart.getLibraries().map(dart.getLibrary);
for (let lib of libs) {
if (lib === THIS) {
return null;
}
} return THIS; })(this)''';
var actualThis =
await debugger.evaluateJsOnCallFrame(callFrameId, findCurrent);
return (actualThis.type == 'undefined')
? null
: Property({'name': 'this', 'value': actualThis});
}