blob: 59c26fda9fb5d3bfe2c42a9b2b6ca05938ad8cd9 [file] [log] [blame]
// Copyright (c) 2022, 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 'package:meta/meta.dart';
import 'serialization.dart';
import 'serialization_extensions.dart';
/// The key used to store the remote instance cache in the current serialization
/// zone (in server mode only).
const Symbol remoteInstanceZoneKey = #remoteInstanceCache;
/// On the server side we keep track of remote instances by their ID.
///
/// These are a part of the current serialization zone, which all serialization
/// and deserialization must be done in.
///
/// This means the cache lifetime is that of the serialization zone it is run
/// in.
Map<int, RemoteInstance> get _remoteInstanceCache =>
Zone.current[remoteInstanceZoneKey];
/// Base class for types that need to be able to be traced back to a specific
/// instance on the server side.
abstract class RemoteInstance implements Serializable {
/// The unique ID for this instance.
final int id;
/// The type of instance being encoded.
RemoteInstanceKind get kind;
/// Static, incrementing ids.
static int _nextId = 0;
/// Gets the next unique identifier.
static int get uniqueId => _nextId++;
/// On the client side [id]s are given and you should reconstruct objects with
/// the given ID. On the server side ids should be created using
/// [RemoteInstance.uniqueId].
RemoteInstance(this.id);
/// Retrieves a cached instance by ID.
static T cached<T>(int id) => _remoteInstanceCache[id] as T;
/// Deserializes an instance based on the current [serializationMode].
static T deserialize<T>(Deserializer deserializer) =>
(deserializer..moveNext()).expectRemoteInstance();
/// This method should be overridden by all subclasses, which should on their
/// first line call this super function.
///
/// They should then return immediately if [serializationMode] is
/// [SerializationMode.client], so that only an ID is sent.
@mustCallSuper
void serialize(Serializer serializer) {
serializer.addNum(id);
switch (serializationMode) {
case SerializationMode.client:
// We only send the ID from the client side.
return;
case SerializationMode.server:
serializer.addNum(kind.index);
_remoteInstanceCache[id] = this;
return;
}
}
}
/// A remote instance which is just a pointer to some server side instance of
/// a generic object.
///
/// The wrapped object is not serialized.
class RemoteInstanceImpl extends RemoteInstance {
/// Always null on the client side, has an actual instance on the server side.
final Object? instance;
@override
RemoteInstanceKind get kind => RemoteInstanceKind.instance;
RemoteInstanceImpl({required int id, this.instance}) : super(id);
}
// The kinds of instances.
enum RemoteInstanceKind {
classDeclaration,
constructorDeclaration,
fieldDeclaration,
functionDeclaration,
functionTypeAnnotation,
methodDeclaration,
namedTypeAnnotation,
parameterDeclaration,
typeAliasDeclaration,
typeParameterDeclaration,
variableDeclaration,
// A generic instance that is just a pointer to some server side instance.
instance,
}