[VM/Service] Add private _deleteIdZone RPC

TEST=pkg/vm_service/test/id_zones_test.dart, CI

Issue: https://github.com/dart-lang/sdk/issues/55869
Change-Id: I0b951505edd98364373d5913b7a01f6d4775998e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/380360
Commit-Queue: Derek Xu <derekx@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Derek Xu
2024-08-28 16:41:50 +00:00
committed by Commit Queue
parent daa8cbb29e
commit 8fa0f56f45
5 changed files with 135 additions and 36 deletions
+41
View File
@@ -263,6 +263,47 @@ final tests = <IsolateTest>[
expect(cInstanceRef3['type'], '@Instance');
expect(cInstanceRef3['id'], 'objects/0/3');
},
// Test deleting an ID Zone.
(VmService service, IsolateRef isolateRef) async {
final isolateId = isolateRef.id!;
final idZone4 = (await service.callMethod(
'_createIdZone',
isolateId: isolateId,
args: {
'backingBufferKind': 'Ring',
'idAssignmentPolicy': 'AlwaysAllocate',
},
))
.json!;
expect(idZone4['type'], '_IdZone');
expect(idZone4['id'], 'zones/4');
expect(idZone4['backingBufferKind'], 'Ring');
expect(idZone4['idAssignmentPolicy'], 'AlwaysAllocate');
await service.callMethod(
'_deleteIdZone',
isolateId: isolateId,
args: {
'_idZoneId': idZone4['id'],
},
);
try {
await service.callMethod(
'evaluateInFrame',
isolateId: isolateId,
args: {
'frameIndex': 0,
'expression': 'c',
'_idZoneId': idZone4['id'],
},
);
fail('successfully used an ID zone that should have been deleted');
} on RPCError catch (e) {
expect(e.code, RPCErrorKind.kInvalidParams.code);
}
},
resumeIsolate,
];
+7
View File
@@ -3023,6 +3023,13 @@ RingServiceIdZone& Isolate::AddServiceIdZone(
}
}
void Isolate::DeleteServiceIdZone(int32_t id) {
ASSERT(service_id_zones_ != nullptr);
ASSERT(id < service_id_zones_->length());
delete service_id_zones_->At(id);
(*service_id_zones_)[id] = nullptr;
}
RingServiceIdZone& Isolate::EnsureDefaultServiceIdZone() {
if (service_id_zones_ == nullptr) {
service_id_zones_ = new MallocGrowableArray<RingServiceIdZone*>();
+2
View File
@@ -1256,6 +1256,8 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
ObjectIdRing::IdPolicy id_assignment_policy,
int32_t capacity);
void DeleteServiceIdZone(int32_t id);
// The default Service ID zone is created lazily; this method returns the
// default Service ID zone, creating it if necessary.
RingServiceIdZone& EnsureDefaultServiceIdZone();
+78 -36
View File
@@ -185,6 +185,38 @@ class NoSuchParameter : public MethodParameter {
#define RUNNABLE_ISOLATE_PARAMETER new RunnableIsolateParameter("isolateId")
#define OBJECT_PARAMETER new IdParameter("objectId", true)
static bool ValidateUIntParameter(const char* value) {
if (value == nullptr) {
return false;
}
for (const char* cp = value; *cp != '\0'; cp++) {
if (*cp < '0' || *cp > '9') {
return false;
}
}
return true;
}
class UIntParameter : public MethodParameter {
public:
UIntParameter(const char* name, bool required)
: MethodParameter(name, required) {}
virtual bool Validate(const char* value) const {
return ValidateUIntParameter(value);
}
static uintptr_t Parse(const char* value) {
if (value == nullptr) {
return -1;
}
char* end_ptr = nullptr;
uintptr_t result = strtoul(value, &end_ptr, 10);
ASSERT(*end_ptr == '\0'); // Parsed full string
return result;
}
};
class EnumListParameter : public MethodParameter {
public:
EnumListParameter(const char* name, bool required, const char* const* enums)
@@ -370,6 +402,14 @@ ServiceIdZone::ServiceIdZone(intptr_t id, ObjectIdRing::IdPolicy policy)
ServiceIdZone::~ServiceIdZone() {}
intptr_t ServiceIdZone::StringIdToInt(const char* id_string) {
if (Utils::StrStartsWith(id_string, "zones/") &&
ValidateUIntParameter(id_string + 6)) {
return UIntParameter::Parse(id_string + 6);
}
return -1;
}
RingServiceIdZone::RingServiceIdZone(intptr_t id,
ObjectIdRing::IdPolicy policy,
int32_t capacity)
@@ -751,34 +791,6 @@ class BoolParameter : public MethodParameter {
}
};
class UIntParameter : public MethodParameter {
public:
UIntParameter(const char* name, bool required)
: MethodParameter(name, required) {}
virtual bool Validate(const char* value) const {
if (value == nullptr) {
return false;
}
for (const char* cp = value; *cp != '\0'; cp++) {
if (*cp < '0' || *cp > '9') {
return false;
}
}
return true;
}
static uintptr_t Parse(const char* value) {
if (value == nullptr) {
return -1;
}
char* end_ptr = nullptr;
uintptr_t result = strtoul(value, &end_ptr, 10);
ASSERT(*end_ptr == '\0'); // Parsed full string
return result;
}
};
class Int64Parameter : public MethodParameter {
public:
Int64Parameter(const char* name, bool required)
@@ -1030,14 +1042,11 @@ ErrorPtr Service::InvokeMethod(Isolate* I,
// are about to create, meaning that it is where temporary Service IDs may
// be allocated by the RPC currently being handled.
RingServiceIdZone* id_zone = &isolate.EnsureDefaultServiceIdZone();
const char* id_zone_arg = js.LookupParam("_idZoneId");
if (id_zone_arg != nullptr) {
// We must create a temporary |UIntParameter| to use the
// |UIntParameter::Validate| helper because it is not a static method.
if (Utils::StrStartsWith(id_zone_arg, "zones/") &&
UIntParameter("temp", true).Validate(id_zone_arg + 6)) {
id_zone =
isolate.GetServiceIdZone(UIntParameter::Parse(id_zone_arg + 6));
const char* id_zone_id_arg = js.LookupParam("_idZoneId");
if (id_zone_id_arg != nullptr) {
intptr_t id_zone_id = ServiceIdZone::StringIdToInt(id_zone_id_arg);
if (id_zone_id != -1) {
id_zone = isolate.GetServiceIdZone(id_zone_id);
} else {
id_zone = nullptr;
}
@@ -5182,6 +5191,37 @@ static void CreateIdZone(Thread* thread, JSONStream* js) {
.PrintJSON(*js);
}
static const MethodParameter* const delete_id_zone_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
nullptr,
};
static void DeleteIdZone(Thread* thread, JSONStream* js) {
ASSERT(thread != nullptr);
ASSERT(js != nullptr);
Isolate* isolate = thread->isolate();
ASSERT(isolate != nullptr);
const char* id_zone_id_arg = js->LookupParam("_idZoneId");
if (id_zone_id_arg == nullptr) {
PrintMissingParamError(js, "_idZoneId");
}
// If the `_idZoneId` argument is not missing, we know that the some
// properties of |id_zone_id| have already been checked in |InvokeMethod|
// (search for `js.set_id_zone(*id_zone)` to find the checks), so we can
// assert these properties to be true below.
intptr_t id_zone_id = ServiceIdZone::StringIdToInt(id_zone_id_arg);
ASSERT(id_zone_id != -1);
ASSERT(id_zone_id < isolate->NumServiceIdZones());
isolate->DeleteServiceIdZone(id_zone_id);
PrintSuccess(js);
}
static const MethodParameter* const get_object_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
new UIntParameter("offset", false),
@@ -6016,6 +6056,8 @@ static const ServiceMethodDescriptor service_methods_[] = {
{ "_compileExpression", CompileExpression, compile_expression_params },
{ "_createIdZone", CreateIdZone,
create_id_zone_params },
{ "_deleteIdZone", DeleteIdZone,
delete_id_zone_params },
{ "_enableProfiler", EnableProfiler,
enable_profiler_params, },
{ "evaluate", Evaluate,
+7
View File
@@ -40,6 +40,13 @@ class ServiceIdZone {
ServiceIdZone(intptr_t id, ObjectIdRing::IdPolicy policy);
virtual ~ServiceIdZone();
// Parses a Service ID zone ID string and returns the corresponding integer
// ID. Or, returns -1 if |id_string| is invalid.
//
// For example, this function will return 5 when called with the argument
// "zones/5".
intptr_t static StringIdToInt(const char* id_string);
intptr_t id() const { return id_; }
ObjectIdRing::IdPolicy policy() const { return policy_; }