| // 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 'dart:async'; |
| import 'dart:typed_data'; |
| |
| import 'json_buffer/json_buffer_builder.dart'; |
| |
| /// Scope for accumulating `dart_model` data for efficient transmission. |
| /// |
| /// A scope introduces buffers that can be written to. Some `dart_model` types |
| /// are then instantiated directly into the buffer instead of on the heap, |
| /// making them ready to send with no further copying. |
| /// |
| /// Run code in a scope by calling [run] or [runAsync] on the scope. |
| enum Scope { |
| /// No scope. |
| /// |
| /// If code is running in a scope it can use `Scope.none` to run code |
| /// without a scope. For example macro scope is about accumulating |
| /// augmentations, but macros also send queries. The `none` scope must be |
| /// used when sending queries. |
| none, |
| |
| /// Server-side scope for handling queries. |
| /// |
| /// Provides a new buffer for the query result. |
| /// |
| /// Cannot be nested. |
| query, |
| |
| /// Client-side scope for macro code to run in. |
| /// |
| /// Provides a new buffer for the macro's augmentations. |
| /// |
| /// Cannot be nested. |
| macro; |
| |
| /// Runs [function] in this scope. |
| T run<T>(T Function() function) { |
| _checkNesting(); |
| return runZoned(function, zoneValues: { |
| _symbol: _ScopeData._(this, this == none ? null : JsonBufferBuilder()) |
| }); |
| } |
| |
| /// Runs [function] in this scope. |
| Future<T> runAsync<T>(Future<T> Function() function) { |
| _checkNesting(); |
| return runZoned(function, zoneValues: { |
| _symbol: _ScopeData._(this, this == none ? null : JsonBufferBuilder()) |
| }); |
| } |
| |
| void _checkNesting() { |
| if (this == Scope.macro || this == Scope.query) { |
| final current = _currentOrNull?.type ?? Scope.none; |
| if (current != Scope.none) { |
| throw StateError('$this cannot be nested in another scope; ' |
| 'but, currently running: $current'); |
| } |
| } |
| } |
| |
| /// Creates a "typed map" in the buffer of the current scope. |
| /// |
| /// If there is no current scope, creates a buffer just for this map, which |
| /// is slow. |
| static Map<String, Object?> createMap(TypedMapSchema schema, |
| [Object? v0, |
| Object? v1, |
| Object? v2, |
| Object? v3, |
| Object? v4, |
| Object? v5, |
| Object? v6, |
| Object? v7]) { |
| final scope = Scope._currentOrNull; |
| final buffer = scope?.buffer ?? JsonBufferBuilder(); |
| return buffer.createTypedMap(schema, v0, v1, v2, v3, v4, v5, v6, v7); |
| } |
| |
| /// Creates a "growable map" in the buffer of the current scope. |
| /// |
| /// If there is no current scope, creates a buffer just for this map, which |
| /// is slow. |
| static Map<String, Object?> createGrowableMap() { |
| final scope = Scope._currentOrNull; |
| final buffer = scope?.buffer ?? JsonBufferBuilder(); |
| return buffer.createGrowableMap(); |
| } |
| |
| /// Serializes [node] using the buffer of the current scope. |
| /// |
| /// Values that are part of [node] and already written to the buffer are not |
| /// copied, but used directly. |
| /// |
| /// Throws if called more than once in the same scope. |
| static Uint8List serializeToBinary(Map<String, Object?> node) { |
| final scope = _currentOrNull; |
| final buffer = scope?.buffer ?? JsonBufferBuilder(); |
| if (buffer.map.isNotEmpty) { |
| throw StateError('Buffer was already used to send: ' |
| '${buffer.map}, tried to use it to send: $node'); |
| } |
| buffer.map.addAll(node); |
| return buffer.serialize(); |
| } |
| |
| static const _symbol = #_dartModelScope; |
| static _ScopeData? get _currentOrNull => Zone.current[_symbol] as _ScopeData?; |
| } |
| |
| /// The data that belongs to a scope. |
| /// |
| /// Whether the scope has a [buffer] is determined by the [type]. |
| class _ScopeData { |
| final Scope type; |
| final JsonBufferBuilder? buffer; |
| |
| _ScopeData._(this.type, this.buffer); |
| } |