flutter/packages/flutter_tools/lib/src/base/context.dart
Todd Volkert a4f2ad984d Fix context.dart to properly handle nested zones (#7189)
This fixes an infinite loop in the code that walks the parent
context chain looking for a variable.

This also includes a fix in build_info.dart whereby if the context
is set but the config is not yet set, we were trying to dereference
null.
2016-12-07 19:51:27 -08:00

82 lines
1.9 KiB
Dart

// Copyright 2016 The Chromium Authors. 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:async';
typedef void ErrorHandler(dynamic error, StackTrace stackTrace);
/// A singleton for application functionality. This singleton can be different
/// on a per-Zone basis.
AppContext get context => Zone.current['context'];
class AppContext {
Map<Type, dynamic> _instances = <Type, dynamic>{};
Zone _zone;
AppContext() : _zone = Zone.current;
bool isSet(Type type) {
if (_instances.containsKey(type))
return true;
AppContext parent = _calcParent(_zone);
return parent != null ? parent.isSet(type) : false;
}
dynamic getVariable(Type type) {
if (_instances.containsKey(type))
return _instances[type];
AppContext parent = _calcParent(_zone);
return parent?.getVariable(type);
}
void setVariable(Type type, dynamic instance) {
_instances[type] = instance;
}
dynamic operator[](Type type) => getVariable(type);
dynamic putIfAbsent(Type type, dynamic ifAbsent()) {
dynamic value = getVariable(type);
if (value != null) {
return value;
}
value = ifAbsent();
setVariable(type, value);
return value;
}
AppContext _calcParent(Zone zone) {
Zone parentZone = zone.parent;
if (parentZone == null)
return null;
AppContext parentContext = parentZone['context'];
return parentContext == this
? _calcParent(parentZone)
: parentContext;
}
dynamic runInZone(dynamic method(), {
ZoneBinaryCallback<dynamic, dynamic, StackTrace> onError
}) {
return runZoned(
() => _run(method),
zoneValues: <String, dynamic>{ 'context': this },
onError: onError
);
}
dynamic _run(dynamic method()) async {
Zone previousZone = _zone;
try {
_zone = Zone.current;
return await method();
} finally {
_zone = previousZone;
}
}
}