1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {assertDefined} from 'common/assert_utils';
18import * as protobuf from 'protobufjs';
19
20export class TamperedMessageType extends protobuf.Type {
21  static tamper(protoType: protobuf.Type): TamperedMessageType {
22    TamperedMessageType.tamperTypeDfs(protoType);
23    return protoType as TamperedMessageType;
24  }
25
26  override fields: {[k: string]: TamperedProtoField} = {};
27
28  private static tamperTypeDfs(protoType: protobuf.Type) {
29    for (const fieldName of Object.keys(protoType.fields)) {
30      const field = protoType.fields[fieldName];
31      TamperedMessageType.tamperFieldDfs(field);
32    }
33  }
34
35  private static tamperFieldDfs(field: protobuf.Field) {
36    //TODO: lookupType/lookupEnum are expensive operations. To avoid calling them many times
37    // during TreeNode Operation loops (e.g. SetFormatters, TranslateIntDef, AddDefaults),
38    // we tamper protobuf.Field and protobuf.Type to provide a path linking a Field with
39    // its corresponding Type, greatly improving latency in building a properties tree
40    if ((field as TamperedProtoField).tamperedMessageType) {
41      return;
42    }
43
44    try {
45      (field as TamperedProtoField).tamperedMessageType =
46        field.parent?.lookupType(field.type) as TamperedMessageType;
47    } catch (e) {
48      // swallow
49    }
50
51    try {
52      (field as TamperedProtoField).tamperedEnumType = field.parent?.lookupEnum(
53        field.type,
54      );
55    } catch (e) {
56      // swallow
57    }
58
59    if ((field as TamperedProtoField).tamperedMessageType === undefined) {
60      return;
61    }
62
63    TamperedMessageType.tamperTypeDfs(
64      assertDefined((field as TamperedProtoField).tamperedMessageType),
65    );
66  }
67}
68
69export class TamperedProtoField extends protobuf.Field {
70  tamperedMessageType: TamperedMessageType | undefined;
71  tamperedEnumType: protobuf.Enum | undefined;
72}
73