blob: fc45704bac60a8b95976a68665452ff092a36374 [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.addInt(id);
// We only send the ID from the client side.
if (serializationMode.isClient) return;
serializer.addInt(kind.index);
_remoteInstanceCache[id] = this;
}
@override
bool operator ==(Object other) => other is RemoteInstance && id == other.id;
}
/// 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
final RemoteInstanceKind kind;
RemoteInstanceImpl({
required int id,
this.instance,
required this.kind,
}) : super(id);
}
// The kinds of instances.
enum RemoteInstanceKind {
classDeclaration,
classIntrospector,
constructorDeclaration,
fieldDeclaration,
functionDeclaration,
functionTypeAnnotation,
identifier,
identifierResolver,
namedStaticType,
methodDeclaration,
namedTypeAnnotation,
parameterDeclaration,
staticType,
typeAliasDeclaration,
typeParameterDeclaration,
typeResolver,
typeDeclarationResolver,
variableDeclaration,
}