b5470ae013
Since the sdk has been unforked, scripts should be updated to generate files by always using NNBD. As such, nnbd_src has been moved to src and the old src has been deleted. This does not address the nnbd tokens e.g. $NULLABLE. That will be done in a future CL. Change-Id: I00bead16a9d19569b07ad0601f581fc14400fdce Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151864 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
1400 lines
44 KiB
Dart
1400 lines
44 KiB
Dart
// Copyright (c) 2012, 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.
|
|
|
|
part of html;
|
|
|
|
class _Property {
|
|
_Property(this.name)
|
|
: _hasValue = false,
|
|
writable = false,
|
|
isMethod = false,
|
|
isOwn = true,
|
|
wasThrown = false;
|
|
|
|
bool get hasValue => _hasValue;
|
|
get value => _value;
|
|
set value(v) {
|
|
_value = v;
|
|
_hasValue = true;
|
|
}
|
|
|
|
final String name;
|
|
Function setter;
|
|
Function getter;
|
|
var _value;
|
|
bool _hasValue;
|
|
bool writable;
|
|
bool isMethod;
|
|
bool isOwn;
|
|
bool wasThrown;
|
|
}
|
|
|
|
/**
|
|
* Manager for navigating between libraries from the devtools console.
|
|
*/
|
|
class _LibraryManager {
|
|
/**
|
|
* Current active library
|
|
*/
|
|
static var _currentLibrary;
|
|
static var _validCache = false;
|
|
|
|
static List<Uri> _libraryUris;
|
|
|
|
// List of all maps to check to determine if there is an exact match.
|
|
static Map<String, List<Uri>> _fastPaths;
|
|
|
|
static void _addFastPath(String key, Uri uri) {
|
|
_fastPaths.putIfAbsent(key, () => <Uri>[]).add(uri);
|
|
}
|
|
|
|
static cache() {
|
|
if (_validCache) return;
|
|
_validCache = true;
|
|
_libraryUris = <Uri>[];
|
|
_fastPaths = new Map<String, List<Uri>>();
|
|
var system = currentMirrorSystem();
|
|
system.libraries.forEach((uri, library) {
|
|
_libraryUris.add(uri);
|
|
_addFastPath(uri.toString(), uri);
|
|
_addFastPath(MirrorSystem.getName(library.simpleName), uri);
|
|
});
|
|
}
|
|
|
|
static String get currentLibrary {
|
|
if (_currentLibrary == null) {
|
|
_currentLibrary =
|
|
currentMirrorSystem().isolate.rootLibrary.uri.toString();
|
|
}
|
|
return _currentLibrary;
|
|
}
|
|
|
|
/**
|
|
* Find libraries matching a given name.
|
|
*
|
|
* Uses heuristics to only return a single match when the user intent is
|
|
* generally unambiguous.
|
|
*/
|
|
static List<Uri> findMatches(String name) {
|
|
cache();
|
|
var nameAsFile = name.endsWith('.dart') ? name : '${name}.dart';
|
|
// Perfect match first.
|
|
var fastPatchMatches = _fastPaths[name];
|
|
if (fastPatchMatches != null) {
|
|
return fastPatchMatches.toList();
|
|
}
|
|
|
|
// Exact match for file path.
|
|
var matches = new LinkedHashSet<Uri>();
|
|
for (var uri in _libraryUris) {
|
|
if (uri.path == name || uri.path == nameAsFile) matches.add(uri);
|
|
}
|
|
if (matches.isNotEmpty) return matches.toList();
|
|
|
|
// Exact match for file name.
|
|
if (name != nameAsFile) {
|
|
for (var uri in _libraryUris) {
|
|
if (uri.pathSegments.isNotEmpty &&
|
|
(uri.pathSegments.last == nameAsFile)) {
|
|
matches.add(uri);
|
|
}
|
|
}
|
|
if (matches.isNotEmpty) return matches.toList();
|
|
}
|
|
|
|
for (var uri in _libraryUris) {
|
|
if (uri.pathSegments.isNotEmpty && (uri.pathSegments.last == name)) {
|
|
matches.add(uri);
|
|
}
|
|
}
|
|
if (matches.isNotEmpty) return matches.toList();
|
|
|
|
// Partial match on path.
|
|
for (var uri in _libraryUris) {
|
|
if (uri.path.contains(name)) {
|
|
matches.add(uri);
|
|
}
|
|
}
|
|
if (matches.isNotEmpty) return matches.toList();
|
|
|
|
// Partial match on entire uri.
|
|
for (var uri in _libraryUris) {
|
|
if (uri.toString().contains(name)) {
|
|
matches.add(uri);
|
|
}
|
|
}
|
|
|
|
if (matches.isNotEmpty) return matches.toList();
|
|
|
|
// Partial match on entire uri ignoring case.
|
|
name = name.toLowerCase();
|
|
for (var uri in _libraryUris) {
|
|
if (uri.toString().toLowerCase().contains(name)) {
|
|
matches.add(uri);
|
|
}
|
|
}
|
|
return matches.toList();
|
|
}
|
|
|
|
static setLibrary([String? name]) {
|
|
// Bust cache in case library list has changed. Ideally we would listen for
|
|
// when libraries are loaded and invalidate based on that.
|
|
_validCache = false;
|
|
cache();
|
|
if (name == null) {
|
|
window.console
|
|
..group("Current library: $_currentLibrary")
|
|
..groupCollapsed("All libraries:");
|
|
_listLibraries();
|
|
window.console..groupEnd()..groupEnd();
|
|
return;
|
|
}
|
|
var matches = findMatches(name);
|
|
if (matches.length != 1) {
|
|
if (matches.length > 1) {
|
|
window.console.warn("Ambiguous library name: $name");
|
|
}
|
|
showMatches(name, matches);
|
|
return;
|
|
}
|
|
_currentLibrary = matches.first.toString();
|
|
window.console.log("Set library to $_currentLibrary");
|
|
}
|
|
|
|
static getLibrary() {
|
|
return currentLibrary;
|
|
}
|
|
|
|
static List<Uri> _sortUris(Iterable<Uri> uris) {
|
|
return (uris.toList())
|
|
..sort((Uri a, Uri b) {
|
|
if (a.scheme != b.scheme) {
|
|
if (a.scheme == 'dart') return -1;
|
|
if (b.scheme == 'dart') return 1;
|
|
return a.scheme.compareTo(b.scheme);
|
|
}
|
|
return a.toString().compareTo(b.toString());
|
|
});
|
|
}
|
|
|
|
static void listLibraries() {
|
|
_validCache = false;
|
|
cache();
|
|
_listLibraries();
|
|
}
|
|
|
|
static void _listLibraries() {
|
|
window.console.log(_sortUris(_libraryUris).join("\n"));
|
|
}
|
|
|
|
// Workaround to allow calling console.log with an arbitrary number of
|
|
// arguments.
|
|
static void _log(List<String> args) {
|
|
js.JsNative.callMethod(window.console, 'log', args);
|
|
}
|
|
|
|
static showMatches(String key, Iterable<Uri> uris) {
|
|
var boldPairs = [];
|
|
var sb = new StringBuffer();
|
|
if (uris.isEmpty) {
|
|
window.console.group("All libraries:");
|
|
_listLibraries();
|
|
window.console
|
|
..groupEnd()
|
|
..error("No library names or URIs match '$key'");
|
|
return;
|
|
}
|
|
sb.write("${uris.length} matches\n");
|
|
var lowerCaseKey = key.toLowerCase();
|
|
for (var uri in uris) {
|
|
var txt = uri.toString();
|
|
int index = txt.toLowerCase().indexOf(lowerCaseKey);
|
|
if (index != -1) {
|
|
// %c enables styling console log messages with css
|
|
// specified at the end of the console.
|
|
sb..write(txt.substring(0, index))..write('%c');
|
|
var matchEnd = index + key.length;
|
|
sb
|
|
..write(txt.substring(index, matchEnd))
|
|
..write('%c')
|
|
..write(txt.substring(matchEnd))
|
|
..write('\n');
|
|
boldPairs..add('font-weight: bold')..add('font-weight: normal');
|
|
}
|
|
}
|
|
_log([sb.toString()]..addAll(boldPairs));
|
|
}
|
|
}
|
|
|
|
class _ConsoleVariables {
|
|
Map<String, Object> _data = new Map<String, Object>();
|
|
|
|
/**
|
|
* Forward member accesses to the backing JavaScript object.
|
|
*/
|
|
noSuchMethod(Invocation invocation) {
|
|
String member = MirrorSystem.getName(invocation.memberName);
|
|
if (invocation.isGetter) {
|
|
return _data[member];
|
|
} else if (invocation.isSetter) {
|
|
assert(member.endsWith('='));
|
|
member = member.substring(0, member.length - 1);
|
|
_data[member] = invocation.positionalArguments[0];
|
|
} else {
|
|
return Function.apply(_data[member], invocation.positionalArguments,
|
|
invocation.namedArguments);
|
|
}
|
|
}
|
|
|
|
void clear() => _data.clear();
|
|
|
|
/**
|
|
* List all variables currently defined.
|
|
*/
|
|
List variables() => _data.keys.toList();
|
|
|
|
void setVariable(String name, value) {
|
|
_data[name] = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Base class for invocation trampolines used to closurize methods, getters
|
|
* and setters.
|
|
*/
|
|
abstract class _Trampoline implements Function {
|
|
final ObjectMirror _receiver;
|
|
final MethodMirror _methodMirror;
|
|
final Symbol _selector;
|
|
|
|
_Trampoline(this._receiver, this._methodMirror, this._selector);
|
|
}
|
|
|
|
class _MethodTrampoline extends _Trampoline {
|
|
_MethodTrampoline(
|
|
ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
|
|
: super(receiver, methodMirror, selector);
|
|
|
|
noSuchMethod(Invocation msg) {
|
|
if (msg.memberName != #call) return super.noSuchMethod(msg);
|
|
return _receiver
|
|
.invoke(_selector, msg.positionalArguments, msg.namedArguments)
|
|
.reflectee;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invocation trampoline class used to closurize getters.
|
|
*/
|
|
class _GetterTrampoline extends _Trampoline {
|
|
_GetterTrampoline(
|
|
ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
|
|
: super(receiver, methodMirror, selector);
|
|
|
|
call() => _receiver.getField(_selector).reflectee;
|
|
}
|
|
|
|
/**
|
|
* Invocation trampoline class used to closurize setters.
|
|
*/
|
|
class _SetterTrampoline extends _Trampoline {
|
|
_SetterTrampoline(
|
|
ObjectMirror receiver, MethodMirror methodMirror, Symbol selector)
|
|
: super(receiver, methodMirror, selector);
|
|
|
|
call(value) {
|
|
_receiver.setField(_selector, value);
|
|
}
|
|
}
|
|
|
|
class _Utils {
|
|
static double dateTimeToDouble(DateTime dateTime) =>
|
|
dateTime.millisecondsSinceEpoch.toDouble();
|
|
static DateTime doubleToDateTime(double dateTime) {
|
|
try {
|
|
return new DateTime.fromMillisecondsSinceEpoch(dateTime.toInt());
|
|
} catch (_) {
|
|
// TODO(antonnm): treat exceptions properly in bindings and
|
|
// find out how to treat NaNs.
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static List convertToList(List list) {
|
|
// FIXME: [possible optimization]: do not copy the array if Dart_IsArray is fine w/ it.
|
|
final length = list.length;
|
|
List result = new List(length);
|
|
result.setRange(0, length, list);
|
|
return result;
|
|
}
|
|
|
|
static List convertMapToList(Map map) {
|
|
List result = [];
|
|
map.forEach((k, v) => result.addAll([k, v]));
|
|
return result;
|
|
}
|
|
|
|
static int convertCanvasElementGetContextMap(Map map) {
|
|
int result = 0;
|
|
if (map['alpha'] == true) result |= 0x01;
|
|
if (map['depth'] == true) result |= 0x02;
|
|
if (map['stencil'] == true) result |= 0x4;
|
|
if (map['antialias'] == true) result |= 0x08;
|
|
if (map['premultipliedAlpha'] == true) result |= 0x10;
|
|
if (map['preserveDrawingBuffer'] == true) result |= 0x20;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void populateMap(Map result, List list) {
|
|
for (int i = 0; i < list.length; i += 2) {
|
|
result[list[i]] = list[i + 1];
|
|
}
|
|
}
|
|
|
|
static bool isMap(obj) => obj is Map;
|
|
|
|
static List toListIfIterable(obj) => obj is Iterable ? obj.toList() : null;
|
|
|
|
static Map createMap() => {};
|
|
|
|
static parseJson(String jsonSource) =>
|
|
const JsonDecoder().convert(jsonSource);
|
|
|
|
static String getLibraryUrl() => _LibraryManager.currentLibrary;
|
|
|
|
static makeUnimplementedError(String fileName, int lineNo) {
|
|
return new UnsupportedError('[info: $fileName:$lineNo]');
|
|
}
|
|
|
|
static bool isTypeSubclassOf(Type type, Type other) {
|
|
if (type == other) {
|
|
return true;
|
|
}
|
|
var superclass = reflectClass(type).superclass;
|
|
if (superclass != null) {
|
|
return isTypeSubclassOf(superclass.reflectedType, other);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static Element getAndValidateNativeType(Type type, String tagName) {
|
|
var element = new Element.tag(tagName);
|
|
if (!isTypeSubclassOf(type, element.runtimeType)) {
|
|
return null;
|
|
}
|
|
return element;
|
|
}
|
|
|
|
static forwardingPrint(String message) =>
|
|
_blink.Blink_Utils.forwardingPrint(message);
|
|
static void spawnDomHelper(Function f, int replyTo) =>
|
|
_blink.Blink_Utils.spawnDomHelper(f, replyTo);
|
|
|
|
// TODO(vsm): Make this API compatible with spawnUri. It should also
|
|
// return a Future<Isolate>.
|
|
// TODO(jacobr): IS THIS RIGHT? I worry we have broken conversion from Promise to Future.
|
|
static spawnDomUri(String uri) => _blink.Blink_Utils.spawnDomUri(uri);
|
|
|
|
// The following methods were added for debugger integration to make working
|
|
// with the Dart C mirrors API simpler.
|
|
// TODO(jacobr): consider moving them to a separate library.
|
|
// If Dart supported dynamic code injection, we would only inject this code
|
|
// when the debugger is invoked.
|
|
|
|
/**
|
|
* Strips the private secret prefix from member names of the form
|
|
* someName@hash.
|
|
*/
|
|
static String stripMemberName(String name) {
|
|
int endIndex = name.indexOf('@');
|
|
return endIndex > 0 ? name.substring(0, endIndex) : name;
|
|
}
|
|
|
|
/**
|
|
* Takes a list containing variable names and corresponding values and
|
|
* returns a map from normalized names to values. Variable names are assumed
|
|
* to have list offsets 2*n values at offset 2*n+1. This method is required
|
|
* because Dart_GetLocalVariables returns a list instead of an object that
|
|
* can be queried to lookup names and values.
|
|
*/
|
|
static Map<String, dynamic> createLocalVariablesMap(List localVariables) {
|
|
var map = {};
|
|
for (int i = 0; i < localVariables.length; i += 2) {
|
|
map[stripMemberName(localVariables[i])] = localVariables[i + 1];
|
|
}
|
|
return map;
|
|
}
|
|
|
|
static _ConsoleVariables _consoleTempVariables = new _ConsoleVariables();
|
|
|
|
/**
|
|
* Takes an [expression] and a list of [local] variable and returns an
|
|
* expression for a closure with a body matching the original expression
|
|
* where locals are passed in as arguments. Returns a list containing the
|
|
* String expression for the closure and the list of arguments that should
|
|
* be passed to it. The expression should then be evaluated using
|
|
* Dart_EvaluateExpr which will generate a closure that should be invoked
|
|
* with the list of arguments passed to this method.
|
|
*
|
|
* For example:
|
|
* <code>
|
|
* _consoleTempVariables = {'a' : someValue, 'b': someOtherValue}
|
|
* wrapExpressionAsClosure("foo + bar + a", ["bar", 40, "foo", 2], true)
|
|
* </code>
|
|
* will return:
|
|
* <code>
|
|
* ["""(final $consoleVariables, final bar, final foo, final a, final b) =>
|
|
* (foo + bar + a
|
|
* )""",
|
|
* [_consoleTempVariables, 40, 2, someValue, someOtherValue]]
|
|
* </code>
|
|
*/
|
|
static List wrapExpressionAsClosure(
|
|
String expression, List locals, bool includeCommandLineAPI) {
|
|
var args = {};
|
|
var sb = new StringBuffer("(");
|
|
addArg(arg, value) {
|
|
arg = stripMemberName(arg);
|
|
if (args.containsKey(arg)) return;
|
|
// We ignore arguments with the name 'this' rather than throwing an
|
|
// exception because Dart_GetLocalVariables includes 'this' and it
|
|
// is more convenient to filter it out here than from C++ code.
|
|
// 'this' needs to be handled by calling Dart_EvaluateExpr with
|
|
// 'this' as the target rather than by passing it as an argument.
|
|
if (arg == 'this') return;
|
|
// Avoid being broken by bogus ':async_op' local passed in when within
|
|
// an async method.
|
|
if (arg.startsWith(':')) return;
|
|
if (args.isNotEmpty) {
|
|
sb.write(", ");
|
|
}
|
|
sb.write("final $arg");
|
|
args[arg] = value;
|
|
}
|
|
|
|
if (includeCommandLineAPI) {
|
|
addArg("\$consoleVariables", _consoleTempVariables);
|
|
|
|
// FIXME: use a real Dart tokenizer. The following regular expressions
|
|
// only allow setting variables at the immediate start of the expression
|
|
// to limit the number of edge cases we have to handle.
|
|
|
|
// Match expressions that start with "var x"
|
|
final _VARIABLE_DECLARATION = new RegExp("^(\\s*)var\\s+(\\w+)");
|
|
// Match expressions that start with "someExistingConsoleVar ="
|
|
final _SET_VARIABLE = new RegExp("^(\\s*)(\\w+)(\\s*=)");
|
|
// Match trailing semicolons.
|
|
final _ENDING_SEMICOLONS = new RegExp("(;\\s*)*\$");
|
|
expression = expression.replaceAllMapped(_VARIABLE_DECLARATION, (match) {
|
|
var variableName = match[2];
|
|
// Set the console variable if it isn't already set.
|
|
if (!_consoleTempVariables._data.containsKey(variableName)) {
|
|
_consoleTempVariables._data[variableName] = null;
|
|
}
|
|
return "${match[1]}\$consoleVariables.${variableName}";
|
|
});
|
|
|
|
expression = expression.replaceAllMapped(_SET_VARIABLE, (match) {
|
|
var variableName = match[2];
|
|
// Only rewrite if the name matches an existing console variable.
|
|
if (_consoleTempVariables._data.containsKey(variableName)) {
|
|
return "${match[1]}\$consoleVariables.${variableName}${match[3]}";
|
|
} else {
|
|
return match[0];
|
|
}
|
|
});
|
|
|
|
// We only allow dart expressions not Dart statements. Silently remove
|
|
// trailing semicolons the user might have added by accident to reduce the
|
|
// number of spurious compile errors.
|
|
expression = expression.replaceFirst(_ENDING_SEMICOLONS, "");
|
|
}
|
|
|
|
if (locals != null) {
|
|
for (int i = 0; i < locals.length; i += 2) {
|
|
addArg(locals[i], locals[i + 1]);
|
|
}
|
|
}
|
|
// Inject all the already defined console variables.
|
|
_consoleTempVariables._data.forEach(addArg);
|
|
|
|
// TODO(jacobr): remove the parentheses around the expression once
|
|
// dartbug.com/13723 is fixed. Currently we wrap expression in parentheses
|
|
// to ensure only valid Dart expressions are allowed. Otherwise the DartVM
|
|
// quietly ignores trailing Dart statements resulting in user confusion
|
|
// when part of an invalid expression they entered is ignored.
|
|
sb..write(') => (\n$expression\n)');
|
|
return [sb.toString(), args.values.toList(growable: false)];
|
|
}
|
|
|
|
static String _getShortSymbolName(
|
|
Symbol symbol, DeclarationMirror declaration) {
|
|
var name = MirrorSystem.getName(symbol);
|
|
if (declaration is MethodMirror) {
|
|
if (declaration.isSetter && name[name.length - 1] == "=") {
|
|
return name.substring(0, name.length - 1);
|
|
}
|
|
if (declaration.isConstructor) {
|
|
return name.substring(name.indexOf('.') + 1);
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Handle special console commands such as $lib and $libs that should not be
|
|
* evaluated as Dart expressions and instead should be interpreted directly.
|
|
* Commands supported:
|
|
* library <-- shows the current library and lists all libraries.
|
|
* library "library_uri" <-- select a specific library
|
|
* library "library_uri_fragment"
|
|
*/
|
|
static bool maybeHandleSpecialConsoleCommand(String expression) {
|
|
expression = expression.trim();
|
|
var setLibraryCommand = r'library ';
|
|
if (expression == r'library') {
|
|
_LibraryManager.setLibrary();
|
|
return true;
|
|
}
|
|
if (expression.startsWith(setLibraryCommand)) {
|
|
expression = expression.substring(setLibraryCommand.length);
|
|
if (expression.length >= 2) {
|
|
String start = expression[0];
|
|
String end = expression[expression.length - 1];
|
|
// TODO(jacobr): maybe we should require quotes.
|
|
if ((start == "'" && end == "'") || (start == '"' && end == '"')) {
|
|
expression = expression.substring(1, expression.length - 1);
|
|
}
|
|
}
|
|
|
|
_LibraryManager.setLibrary(expression);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of completions to use if the receiver is o.
|
|
*/
|
|
static List<String> getCompletions(o) {
|
|
MirrorSystem system = currentMirrorSystem();
|
|
var completions = new Set<String>();
|
|
addAll(Map<Symbol, dynamic> map, bool isStatic) {
|
|
map.forEach((symbol, mirror) {
|
|
if (mirror.isStatic == isStatic && !mirror.isPrivate) {
|
|
var name = MirrorSystem.getName(symbol);
|
|
if (mirror is MethodMirror && mirror.isSetter)
|
|
name = name.substring(0, name.length - 1);
|
|
completions.add(name);
|
|
}
|
|
});
|
|
}
|
|
|
|
addForClass(ClassMirror mirror, bool isStatic) {
|
|
if (mirror == null) return;
|
|
addAll(mirror.declarations, isStatic);
|
|
if (mirror.superclass != null) addForClass(mirror.superclass, isStatic);
|
|
for (var interface in mirror.superinterfaces) {
|
|
addForClass(interface, isStatic);
|
|
}
|
|
}
|
|
|
|
if (o is Type) {
|
|
addForClass(reflectClass(o), true);
|
|
} else {
|
|
addForClass(reflect(o).type, false);
|
|
}
|
|
return completions.toList(growable: false);
|
|
}
|
|
|
|
/**
|
|
* Adds all candidate String completions from [declarations] to [output]
|
|
* filtering based on [staticContext] and [includePrivate].
|
|
*/
|
|
static void _getCompletionsHelper(ClassMirror classMirror, bool staticContext,
|
|
LibraryMirror libraryMirror, Set<String> output) {
|
|
bool includePrivate = libraryMirror == classMirror.owner;
|
|
classMirror.declarations.forEach((symbol, declaration) {
|
|
if (!includePrivate && declaration.isPrivate) return;
|
|
if (declaration is VariableMirror) {
|
|
if (staticContext != declaration.isStatic) return;
|
|
} else if (declaration is MethodMirror) {
|
|
if (declaration.isOperator) return;
|
|
if (declaration.isConstructor) {
|
|
if (!staticContext) return;
|
|
var name = MirrorSystem.getName(declaration.constructorName);
|
|
if (name.isNotEmpty) output.add(name);
|
|
return;
|
|
}
|
|
if (staticContext != declaration.isStatic) return;
|
|
} else if (declaration is TypeMirror) {
|
|
return;
|
|
}
|
|
output.add(_getShortSymbolName(symbol, declaration));
|
|
});
|
|
|
|
if (!staticContext) {
|
|
for (var interface in classMirror.superinterfaces) {
|
|
_getCompletionsHelper(interface, staticContext, libraryMirror, output);
|
|
}
|
|
if (classMirror.superclass != null) {
|
|
_getCompletionsHelper(
|
|
classMirror.superclass, staticContext, libraryMirror, output);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _getLibraryCompletionsHelper(
|
|
LibraryMirror library, bool includePrivate, Set<String> output) {
|
|
library.declarations.forEach((symbol, declaration) {
|
|
if (!includePrivate && declaration.isPrivate) return;
|
|
output.add(_getShortSymbolName(symbol, declaration));
|
|
});
|
|
}
|
|
|
|
static LibraryMirror getLibraryMirror(String url) =>
|
|
currentMirrorSystem().libraries[Uri.parse(url)];
|
|
|
|
/**
|
|
* Get code completions for [o] only showing privates from [libraryUrl].
|
|
*/
|
|
static List<String> getObjectCompletions(o, String libraryUrl) {
|
|
var classMirror;
|
|
bool staticContext;
|
|
if (o is Type) {
|
|
classMirror = reflectClass(o);
|
|
staticContext = true;
|
|
} else {
|
|
classMirror = reflect(o).type;
|
|
staticContext = false;
|
|
}
|
|
var names = new Set<String>();
|
|
getClassCompletions(classMirror, names, staticContext, libraryUrl);
|
|
return names.toList()..sort();
|
|
}
|
|
|
|
static void getClassCompletions(ClassMirror classMirror, Set<String> names,
|
|
bool staticContext, String libraryUrl) {
|
|
LibraryMirror libraryMirror = getLibraryMirror(libraryUrl);
|
|
_getCompletionsHelper(classMirror, staticContext, libraryMirror, names);
|
|
}
|
|
|
|
static List<String> getLibraryCompletions(String url) {
|
|
var names = new Set<String>();
|
|
_getLibraryCompletionsHelper(getLibraryMirror(url), true, names);
|
|
return names.toList();
|
|
}
|
|
|
|
/**
|
|
* Get valid code completions from within a library and all libraries
|
|
* imported by that library.
|
|
*/
|
|
static List<String> getLibraryCompletionsIncludingImports(String url) {
|
|
var names = new Set<String>();
|
|
var libraryMirror = getLibraryMirror(url);
|
|
_getLibraryCompletionsHelper(libraryMirror, true, names);
|
|
for (var dependency in libraryMirror.libraryDependencies) {
|
|
if (dependency.isImport) {
|
|
if (dependency.prefix == null) {
|
|
_getLibraryCompletionsHelper(dependency.targetLibrary, false, names);
|
|
} else {
|
|
names.add(MirrorSystem.getName(dependency.prefix));
|
|
}
|
|
}
|
|
}
|
|
return names.toList();
|
|
}
|
|
|
|
static final SIDE_EFFECT_FREE_LIBRARIES = new Set<String>()
|
|
..add('dart:html')
|
|
..add('dart:indexed_db')
|
|
..add('dart:svg')
|
|
..add('dart:typed_data')
|
|
..add('dart:web_audio')
|
|
..add('dart:web_gl')
|
|
..add('dart:web_sql');
|
|
|
|
static LibraryMirror _getLibrary(MethodMirror methodMirror) {
|
|
var owner = methodMirror.owner;
|
|
if (owner is ClassMirror) {
|
|
return owner;
|
|
} else if (owner is LibraryMirror) {
|
|
return owner;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* For parity with the JavaScript debugger, we treat some getters as if
|
|
* they are fields so that users can see their values immediately.
|
|
* This matches JavaScript's behavior for getters on DOM objects.
|
|
* In the future we should consider adding an annotation to tag getters
|
|
* in user libraries as side effect free.
|
|
*/
|
|
static bool _isSideEffectFreeGetter(
|
|
MethodMirror methodMirror, LibraryMirror libraryMirror) {
|
|
// This matches JavaScript behavior. We should consider displaying
|
|
// getters for all dart platform libraries rather than just the DOM
|
|
// libraries.
|
|
return libraryMirror.uri.scheme == 'dart' &&
|
|
SIDE_EFFECT_FREE_LIBRARIES.contains(libraryMirror.uri.toString());
|
|
}
|
|
|
|
/**
|
|
* Whether we should treat a property as a field for the purposes of the
|
|
* debugger.
|
|
*/
|
|
static bool treatPropertyAsField(
|
|
MethodMirror methodMirror, LibraryMirror libraryMirror) {
|
|
return (methodMirror.isGetter || methodMirror.isSetter) &&
|
|
(methodMirror.isSynthetic ||
|
|
_isSideEffectFreeGetter(methodMirror, libraryMirror));
|
|
}
|
|
|
|
// TODO(jacobr): generate more concise function descriptions instead of
|
|
// dumping the entire function source.
|
|
static String describeFunction(function) {
|
|
if (function is _Trampoline) return function._methodMirror.source;
|
|
try {
|
|
var mirror = reflect(function);
|
|
return mirror.function.source;
|
|
} catch (e) {
|
|
return function.toString();
|
|
}
|
|
}
|
|
|
|
static List getInvocationTrampolineDetails(_Trampoline method) {
|
|
var loc = method._methodMirror.location;
|
|
return [
|
|
loc.line,
|
|
loc.column,
|
|
loc.sourceUri.toString(),
|
|
MirrorSystem.getName(method._selector)
|
|
];
|
|
}
|
|
|
|
static List getLibraryProperties(
|
|
String libraryUrl, bool ownProperties, bool accessorPropertiesOnly) {
|
|
var properties = new Map<String, _Property>();
|
|
var libraryMirror = getLibraryMirror(libraryUrl);
|
|
_addInstanceMirrors(
|
|
libraryMirror,
|
|
libraryMirror,
|
|
libraryMirror.declarations,
|
|
ownProperties,
|
|
accessorPropertiesOnly,
|
|
false,
|
|
false,
|
|
properties);
|
|
if (!accessorPropertiesOnly) {
|
|
// We need to add class properties for all classes in the library.
|
|
libraryMirror.declarations.forEach((symbol, declarationMirror) {
|
|
if (declarationMirror is ClassMirror) {
|
|
var name = MirrorSystem.getName(symbol);
|
|
if (declarationMirror.hasReflectedType &&
|
|
!properties.containsKey(name)) {
|
|
properties[name] = new _Property(name)
|
|
..value = declarationMirror.reflectedType;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return packageProperties(properties);
|
|
}
|
|
|
|
static List getObjectProperties(
|
|
o, bool ownProperties, bool accessorPropertiesOnly) {
|
|
var properties = new Map<String, _Property>();
|
|
var names = new Set<String>();
|
|
var objectMirror = reflect(o);
|
|
var classMirror = objectMirror.type;
|
|
_addInstanceMirrors(
|
|
objectMirror,
|
|
classMirror.owner,
|
|
classMirror.instanceMembers,
|
|
ownProperties,
|
|
accessorPropertiesOnly,
|
|
false,
|
|
true,
|
|
properties);
|
|
return packageProperties(properties);
|
|
}
|
|
|
|
static List getObjectClassProperties(
|
|
o, bool ownProperties, bool accessorPropertiesOnly) {
|
|
var properties = new Map<String, _Property>();
|
|
var objectMirror = reflect(o);
|
|
var classMirror = objectMirror.type;
|
|
_addInstanceMirrors(
|
|
objectMirror,
|
|
classMirror.owner,
|
|
classMirror.instanceMembers,
|
|
ownProperties,
|
|
accessorPropertiesOnly,
|
|
true,
|
|
false,
|
|
properties);
|
|
_addStatics(classMirror, properties, accessorPropertiesOnly);
|
|
return packageProperties(properties);
|
|
}
|
|
|
|
static List getClassProperties(
|
|
Type t, bool ownProperties, bool accessorPropertiesOnly) {
|
|
var properties = new Map<String, _Property>();
|
|
var classMirror = reflectClass(t);
|
|
_addStatics(classMirror, properties, accessorPropertiesOnly);
|
|
return packageProperties(properties);
|
|
}
|
|
|
|
static void _addStatics(ClassMirror classMirror,
|
|
Map<String, _Property> properties, bool accessorPropertiesOnly) {
|
|
var libraryMirror = classMirror.owner;
|
|
classMirror.declarations.forEach((symbol, declaration) {
|
|
var name = _getShortSymbolName(symbol, declaration);
|
|
if (name.isEmpty) return;
|
|
if (declaration is VariableMirror) {
|
|
if (accessorPropertiesOnly) return;
|
|
if (!declaration.isStatic) return;
|
|
properties.putIfAbsent(name, () => new _Property(name))
|
|
..value = classMirror.getField(symbol).reflectee
|
|
..writable = !declaration.isFinal && !declaration.isConst;
|
|
} else if (declaration is MethodMirror) {
|
|
MethodMirror methodMirror = declaration;
|
|
// FIXMEDART: should we display constructors?
|
|
if (methodMirror.isConstructor) return;
|
|
if (!methodMirror.isStatic) return;
|
|
if (accessorPropertiesOnly) {
|
|
if (methodMirror.isRegularMethod ||
|
|
treatPropertyAsField(methodMirror, libraryMirror)) {
|
|
return;
|
|
}
|
|
} else if (!methodMirror.isRegularMethod &&
|
|
!treatPropertyAsField(methodMirror, libraryMirror)) {
|
|
return;
|
|
}
|
|
var property = properties.putIfAbsent(name, () => new _Property(name));
|
|
_fillMethodMirrorProperty(libraryMirror, classMirror, methodMirror,
|
|
symbol, accessorPropertiesOnly, property);
|
|
}
|
|
});
|
|
}
|
|
|
|
static void _fillMethodMirrorProperty(
|
|
LibraryMirror libraryMirror,
|
|
methodOwner,
|
|
MethodMirror methodMirror,
|
|
Symbol symbol,
|
|
bool accessorPropertiesOnly,
|
|
_Property property) {
|
|
if (methodMirror.isRegularMethod) {
|
|
property
|
|
..value = new _MethodTrampoline(methodOwner, methodMirror, symbol)
|
|
..isMethod = true;
|
|
} else if (methodMirror.isGetter) {
|
|
if (treatPropertyAsField(methodMirror, libraryMirror)) {
|
|
try {
|
|
property.value = methodOwner.getField(symbol).reflectee;
|
|
} catch (e) {
|
|
property
|
|
..wasThrown = true
|
|
..value = e;
|
|
}
|
|
} else if (accessorPropertiesOnly) {
|
|
property.getter =
|
|
new _GetterTrampoline(methodOwner, methodMirror, symbol);
|
|
}
|
|
} else if (methodMirror.isSetter) {
|
|
if (accessorPropertiesOnly &&
|
|
!treatPropertyAsField(methodMirror, libraryMirror)) {
|
|
property.setter = new _SetterTrampoline(methodOwner, methodMirror,
|
|
MirrorSystem.getSymbol(property.name, libraryMirror));
|
|
}
|
|
property.writable = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper method that handles collecting up properties from classes
|
|
* or libraries using the filters [ownProperties], [accessorPropertiesOnly],
|
|
* [hideFields], and [hideMethods] to determine which properties are
|
|
* collected. [accessorPropertiesOnly] specifies whether all properties
|
|
* should be returned or just accessors. [hideFields] specifies whether
|
|
* fields should be hidden. hideMethods specifies whether methods should be
|
|
* shown or hidden. [ownProperties] is not currently used but is part of the
|
|
* Blink devtools API for enumerating properties.
|
|
*/
|
|
static void _addInstanceMirrors(
|
|
ObjectMirror objectMirror,
|
|
LibraryMirror libraryMirror,
|
|
Map<Symbol, Mirror> declarations,
|
|
bool ownProperties,
|
|
bool accessorPropertiesOnly,
|
|
bool hideFields,
|
|
bool hideMethods,
|
|
Map<String, _Property> properties) {
|
|
declarations.forEach((symbol, declaration) {
|
|
if (declaration is TypedefMirror || declaration is ClassMirror) return;
|
|
var name = _getShortSymbolName(symbol, declaration);
|
|
if (name.isEmpty) return;
|
|
bool isField = declaration is VariableMirror ||
|
|
(declaration is MethodMirror &&
|
|
treatPropertyAsField(declaration, libraryMirror));
|
|
if ((isField && hideFields) || (hideMethods && !isField)) return;
|
|
if (accessorPropertiesOnly) {
|
|
if (declaration is VariableMirror ||
|
|
declaration.isRegularMethod ||
|
|
isField) {
|
|
return;
|
|
}
|
|
} else if (declaration is MethodMirror &&
|
|
(declaration.isGetter || declaration.isSetter) &&
|
|
!treatPropertyAsField(declaration, libraryMirror)) {
|
|
return;
|
|
}
|
|
var property = properties.putIfAbsent(name, () => new _Property(name));
|
|
if (declaration is VariableMirror) {
|
|
property
|
|
..value = objectMirror.getField(symbol).reflectee
|
|
..writable = !declaration.isFinal && !declaration.isConst;
|
|
return;
|
|
}
|
|
_fillMethodMirrorProperty(libraryMirror, objectMirror, declaration,
|
|
symbol, accessorPropertiesOnly, property);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Flatten down the properties data structure into a List that is easy to
|
|
* access from native code.
|
|
*/
|
|
static List packageProperties(Map<String, _Property> properties) {
|
|
var ret = [];
|
|
for (var property in properties.values) {
|
|
ret.addAll([
|
|
property.name,
|
|
property.setter,
|
|
property.getter,
|
|
property.value,
|
|
property.hasValue,
|
|
property.writable,
|
|
property.isMethod,
|
|
property.isOwn,
|
|
property.wasThrown
|
|
]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Get a property, returning null if the property does not exist.
|
|
* For private property names, we attempt to resolve the property in the
|
|
* context of each library that the property name could be associated with.
|
|
*/
|
|
static getObjectPropertySafe(o, String propertyName) {
|
|
var objectMirror = reflect(o);
|
|
var classMirror = objectMirror.type;
|
|
if (propertyName.startsWith("_")) {
|
|
var attemptedLibraries = new Set<LibraryMirror>();
|
|
while (classMirror != null) {
|
|
LibraryMirror library = classMirror.owner;
|
|
if (!attemptedLibraries.contains(library)) {
|
|
try {
|
|
return objectMirror
|
|
.getField(MirrorSystem.getSymbol(propertyName, library))
|
|
.reflectee;
|
|
} catch (e) {}
|
|
attemptedLibraries.add(library);
|
|
}
|
|
classMirror = classMirror.superclass;
|
|
}
|
|
return null;
|
|
}
|
|
try {
|
|
return objectMirror
|
|
.getField(MirrorSystem.getSymbol(propertyName))
|
|
.reflectee;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to wrap the inspect method on InjectedScriptHost to provide the
|
|
* inspect method required for the
|
|
*/
|
|
static List consoleApi(host) {
|
|
return [
|
|
"inspect",
|
|
(o) {
|
|
js.JsNative.callMethod(host, "_inspect", [o]);
|
|
return o;
|
|
},
|
|
"dir",
|
|
window.console.dir,
|
|
"dirxml",
|
|
window.console.dirxml
|
|
// FIXME: add copy method.
|
|
];
|
|
}
|
|
|
|
static List getMapKeyList(Map map) => map.keys.toList();
|
|
|
|
static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
|
|
|
|
static void register(
|
|
Document document, String tag, Type type, String extendsTagName) {
|
|
var nativeClass = _validateCustomType(type);
|
|
|
|
if (extendsTagName == null) {
|
|
if (nativeClass.reflectedType != HtmlElement) {
|
|
throw new UnsupportedError('Class must provide extendsTag if base '
|
|
'native class is not HTMLElement');
|
|
}
|
|
}
|
|
|
|
_register(document, tag, type, extendsTagName);
|
|
}
|
|
|
|
static void _register(Document document, String tag, Type customType,
|
|
String extendsTagName) =>
|
|
_blink.Blink_Utils.register(document, tag, customType, extendsTagName);
|
|
|
|
static Element createElement(Document document, String tagName) =>
|
|
_blink.Blink_Utils.createElement(document, tagName);
|
|
}
|
|
|
|
class _DOMWindowCrossFrame extends DartHtmlDomObject implements WindowBase {
|
|
_DOMWindowCrossFrame.internal();
|
|
|
|
static _createSafe(win) {
|
|
if (identical(win, window)) {
|
|
// The current Window object is the only window object that should not
|
|
// use _DOMWindowCrossFrame.
|
|
return window;
|
|
}
|
|
return win is _DOMWindowCrossFrame
|
|
? win
|
|
: _blink.Blink_Utils.setInstanceInterceptor(win, _DOMWindowCrossFrame);
|
|
}
|
|
|
|
// Fields.
|
|
HistoryBase get history {
|
|
var history = _blink.BlinkWindow.instance.history_Getter_(this);
|
|
return history is _HistoryCrossFrame
|
|
? history
|
|
: _blink.Blink_Utils.setInstanceInterceptor(
|
|
history, _HistoryCrossFrame);
|
|
}
|
|
|
|
LocationBase get location {
|
|
var location = _blink.BlinkWindow.instance.location_Getter_(this);
|
|
return location is _LocationCrossFrame
|
|
? location
|
|
: _blink.Blink_Utils.setInstanceInterceptor(
|
|
location, _LocationCrossFrame);
|
|
}
|
|
|
|
bool get closed => _blink.BlinkWindow.instance.closed_Getter_(this);
|
|
WindowBase get opener => _convertNativeToDart_Window(
|
|
_blink.BlinkWindow.instance.opener_Getter_(this));
|
|
WindowBase get parent => _convertNativeToDart_Window(
|
|
_blink.BlinkWindow.instance.parent_Getter_(this));
|
|
WindowBase get top => _convertNativeToDart_Window(
|
|
_blink.BlinkWindow.instance.top_Getter_(this));
|
|
|
|
// Methods.
|
|
void close() => _blink.BlinkWindow.instance.close_Callback_0_(this);
|
|
void postMessage(Object message, String targetOrigin,
|
|
[List<MessagePort>? transfer]) =>
|
|
_blink.BlinkWindow.instance.postMessage_Callback_3_(
|
|
this,
|
|
convertDartToNative_SerializedScriptValue(message),
|
|
targetOrigin,
|
|
transfer);
|
|
|
|
// Implementation support.
|
|
String get typeName => "Window";
|
|
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
Events get on => throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
void _addEventListener(
|
|
[String? type, EventListener? listener, bool? useCapture]) =>
|
|
throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
void addEventListener(String type, EventListener? listener,
|
|
[bool? useCapture]) =>
|
|
throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
bool dispatchEvent(Event event) => throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
void _removeEventListener(
|
|
[String? type, EventListener? listener, bool? useCapture]) =>
|
|
throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
// TODO(efortuna): Remove this method. dartbug.com/16814
|
|
void removeEventListener(String type, EventListener? listener,
|
|
[bool? useCapture]) =>
|
|
throw new UnsupportedError(
|
|
'You can only attach EventListeners to your own window.');
|
|
}
|
|
|
|
class _HistoryCrossFrame extends DartHtmlDomObject implements HistoryBase {
|
|
_HistoryCrossFrame.internal();
|
|
|
|
// Methods.
|
|
void back() => _blink.BlinkHistory.instance.back_Callback_0_(this);
|
|
void forward() => _blink.BlinkHistory.instance.forward_Callback_0_(this);
|
|
void go([int? delta]) {
|
|
if (delta != null) {
|
|
_blink.BlinkHistory.instance.go_Callback_1_(this, delta);
|
|
return;
|
|
}
|
|
_blink.BlinkHistory.instance.go_Callback_0_(this);
|
|
return;
|
|
}
|
|
|
|
// Implementation support.
|
|
String get typeName => "History";
|
|
}
|
|
|
|
class _LocationCrossFrame extends DartHtmlDomObject implements LocationBase {
|
|
_LocationCrossFrame.internal();
|
|
|
|
// Fields.
|
|
set href(String value) =>
|
|
_blink.BlinkLocation.instance.href_Setter_(this, value);
|
|
|
|
// Implementation support.
|
|
String get typeName => "Location";
|
|
}
|
|
|
|
// TODO(vsm): Remove DOM isolate code once we have Dartium isolates
|
|
// as workers. This is only used to support
|
|
// printing and timers in background isolates. As workers they should
|
|
// be able to just do those things natively.
|
|
|
|
_makeSendPortFuture(spawnRequest) {
|
|
final completer = new Completer<SendPort>.sync();
|
|
final port = new ReceivePort();
|
|
port.listen((result) {
|
|
completer.complete(result);
|
|
port.close();
|
|
});
|
|
// TODO: SendPort.hashCode is ugly way to access port id.
|
|
spawnRequest(port.sendPort.hashCode);
|
|
return completer.future;
|
|
}
|
|
|
|
Future<SendPort> _spawnDomHelper(Function f) => _makeSendPortFuture((portId) {
|
|
_Utils.spawnDomHelper(f, portId);
|
|
});
|
|
|
|
final Future<SendPort> __HELPER_ISOLATE_PORT =
|
|
_spawnDomHelper(_helperIsolateMain);
|
|
|
|
// Tricky part.
|
|
// Once __HELPER_ISOLATE_PORT gets resolved, it will still delay in .then
|
|
// and to delay Timer.run is used. However, Timer.run will try to register
|
|
// another Timer and here we got stuck: event cannot be posted as then
|
|
// callback is not executed because it's delayed with timer.
|
|
// Therefore once future is resolved, it's unsafe to call .then on it
|
|
// in Timer code.
|
|
SendPort __SEND_PORT;
|
|
|
|
_sendToHelperIsolate(msg, SendPort replyTo) {
|
|
if (__SEND_PORT != null) {
|
|
__SEND_PORT.send([msg, replyTo]);
|
|
} else {
|
|
__HELPER_ISOLATE_PORT.then((port) {
|
|
__SEND_PORT = port;
|
|
__SEND_PORT.send([msg, replyTo]);
|
|
});
|
|
}
|
|
}
|
|
|
|
final _TIMER_REGISTRY = new Map<SendPort, Timer>();
|
|
|
|
const _NEW_TIMER = 'NEW_TIMER';
|
|
const _CANCEL_TIMER = 'CANCEL_TIMER';
|
|
const _TIMER_PING = 'TIMER_PING';
|
|
const _PRINT = 'PRINT';
|
|
|
|
_helperIsolateMain(originalSendPort) {
|
|
var port = new ReceivePort();
|
|
originalSendPort.send(port.sendPort);
|
|
port.listen((args) {
|
|
var msg = args.first;
|
|
var replyTo = args.last;
|
|
final cmd = msg[0];
|
|
if (cmd == _NEW_TIMER) {
|
|
final duration = new Duration(milliseconds: msg[1]);
|
|
bool periodic = msg[2];
|
|
ping() {
|
|
replyTo.send(_TIMER_PING);
|
|
}
|
|
|
|
;
|
|
_TIMER_REGISTRY[replyTo] = periodic
|
|
? new Timer.periodic(duration, (_) {
|
|
ping();
|
|
})
|
|
: new Timer(duration, ping);
|
|
} else if (cmd == _CANCEL_TIMER) {
|
|
_TIMER_REGISTRY.remove(replyTo).cancel();
|
|
} else if (cmd == _PRINT) {
|
|
final message = msg[1];
|
|
// TODO(antonm): we need somehow identify those isolates.
|
|
print('[From isolate] $message');
|
|
}
|
|
});
|
|
}
|
|
|
|
final _printClosure = (s) => window.console.log(s);
|
|
final _pureIsolatePrintClosure = (s) {
|
|
_sendToHelperIsolate([_PRINT, s], null);
|
|
};
|
|
|
|
final _forwardingPrintClosure = _Utils.forwardingPrint;
|
|
|
|
final _uriBaseClosure = () => Uri.parse(window.location.href);
|
|
|
|
final _pureIsolateUriBaseClosure = () {
|
|
throw new UnimplementedError("Uri.base on a background isolate "
|
|
"is not supported in the browser");
|
|
};
|
|
|
|
class _Timer implements Timer {
|
|
static const int _STATE_TIMEOUT = 0;
|
|
static const int _STATE_INTERVAL = 1;
|
|
int _state;
|
|
|
|
_Timer(int milliSeconds, void callback(Timer timer), bool repeating) {
|
|
if (repeating) {
|
|
_state = (window._setInterval(() {
|
|
callback(this);
|
|
}, milliSeconds) <<
|
|
1) |
|
|
_STATE_INTERVAL;
|
|
} else {
|
|
_state = (window._setTimeout(() {
|
|
_state = null;
|
|
callback(this);
|
|
}, milliSeconds) <<
|
|
1) |
|
|
_STATE_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
void cancel() {
|
|
if (_state == null) return;
|
|
int id = _state >> 1;
|
|
if ((_state & 1) == _STATE_TIMEOUT) {
|
|
window._clearTimeout(id);
|
|
} else {
|
|
window._clearInterval(id);
|
|
}
|
|
_state = null;
|
|
}
|
|
|
|
bool get isActive => _state != null;
|
|
}
|
|
|
|
get _timerFactoryClosure =>
|
|
(int milliSeconds, void callback(Timer timer), bool repeating) {
|
|
return new _Timer(milliSeconds, callback, repeating);
|
|
};
|
|
|
|
class _PureIsolateTimer implements Timer {
|
|
bool _isActive = true;
|
|
final ReceivePort _port = new ReceivePort();
|
|
SendPort _sendPort; // Effectively final.
|
|
|
|
// static SendPort _SEND_PORT;
|
|
|
|
_PureIsolateTimer(int milliSeconds, callback, repeating) {
|
|
_sendPort = _port.sendPort;
|
|
_port.listen((msg) {
|
|
assert(msg == _TIMER_PING);
|
|
_isActive = repeating;
|
|
callback(this);
|
|
if (!repeating) _cancel();
|
|
});
|
|
|
|
_send([_NEW_TIMER, milliSeconds, repeating]);
|
|
}
|
|
|
|
void cancel() {
|
|
_cancel();
|
|
_send([_CANCEL_TIMER]);
|
|
}
|
|
|
|
void _cancel() {
|
|
_isActive = false;
|
|
_port.close();
|
|
}
|
|
|
|
_send(msg) {
|
|
_sendToHelperIsolate(msg, _sendPort);
|
|
}
|
|
|
|
bool get isActive => _isActive;
|
|
}
|
|
|
|
get _pureIsolateTimerFactoryClosure =>
|
|
((int milliSeconds, void callback(Timer time), bool repeating) =>
|
|
new _PureIsolateTimer(milliSeconds, callback, repeating));
|
|
|
|
class _ScheduleImmediateHelper {
|
|
MutationObserver _observer;
|
|
final DivElement _div = new DivElement();
|
|
Function _callback;
|
|
|
|
_ScheduleImmediateHelper() {
|
|
// Run in the root-zone as the DOM callback would otherwise execute in the
|
|
// current zone.
|
|
Zone.root.run(() {
|
|
// Mutation events get fired as soon as the current event stack is unwound
|
|
// so we just make a dummy event and listen for that.
|
|
_observer = new MutationObserver(_handleMutation);
|
|
_observer.observe(_div, attributes: true);
|
|
});
|
|
}
|
|
|
|
void _schedule(callback) {
|
|
if (_callback != null) {
|
|
throw new StateError(
|
|
'Only one immediate callback can be scheduled at once');
|
|
}
|
|
_callback = callback;
|
|
// Toggle it to trigger the mutation event.
|
|
_div.hidden = !_div.hidden;
|
|
}
|
|
|
|
_handleMutation(List<MutationRecord> mutations, MutationObserver observer) {
|
|
var tmp = _callback;
|
|
_callback = null;
|
|
tmp();
|
|
}
|
|
}
|
|
|
|
final _ScheduleImmediateHelper _scheduleImmediateHelper =
|
|
new _ScheduleImmediateHelper();
|
|
|
|
get _scheduleImmediateClosure => (void callback()) {
|
|
_scheduleImmediateHelper._schedule(callback);
|
|
};
|
|
|
|
get _pureIsolateScheduleImmediateClosure => ((void callback()) =>
|
|
throw new UnimplementedError("scheduleMicrotask in background isolates "
|
|
"are not supported in the browser"));
|
|
|
|
// Class for unsupported native browser 'DOM' objects.
|
|
class _UnsupportedBrowserObject extends DartHtmlDomObject {}
|