From 85255f2ba556c3e2f49ea35f2245882f2b3202ce Mon Sep 17 00:00:00 2001 From: Fred Chow Date: Sat, 16 Jul 2022 09:39:39 -0700 Subject: [PATCH 1/2] Added representation of enumeration in Maple IR Defined data structures. Implemented ascii and binary input and output. --- src/mapleall/maple_ir/BUILD.gn | 1 + .../maple_ir/include/bin_mpl_export.h | 7 +- .../maple_ir/include/bin_mpl_import.h | 2 + src/mapleall/maple_ir/include/global_tables.h | 6 ++ src/mapleall/maple_ir/include/keywords.def | 1 + .../maple_ir/include/mir_enumeration.h | 57 ++++++++++++++ src/mapleall/maple_ir/include/mir_parser.h | 1 + src/mapleall/maple_ir/src/bin_mpl_export.cpp | 35 ++++++++- src/mapleall/maple_ir/src/bin_mpl_import.cpp | 30 ++++++++ src/mapleall/maple_ir/src/mir_enumeration.cpp | 48 ++++++++++++ src/mapleall/maple_ir/src/mir_module.cpp | 1 + src/mapleall/maple_ir/src/parser.cpp | 77 +++++++++++++++++++ 12 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 src/mapleall/maple_ir/include/mir_enumeration.h create mode 100644 src/mapleall/maple_ir/src/mir_enumeration.cpp diff --git a/src/mapleall/maple_ir/BUILD.gn b/src/mapleall/maple_ir/BUILD.gn index e9d028ba0e..4f0428f9cf 100755 --- a/src/mapleall/maple_ir/BUILD.gn +++ b/src/mapleall/maple_ir/BUILD.gn @@ -43,6 +43,7 @@ src_libmplir = [ "src/mir_nodes.cpp", "src/mir_symbol.cpp", "src/mir_type.cpp", + "src/mir_enumeration.cpp", "src/opcode_info.cpp", "src/option.cpp", "src/mpl2mpl_options.cpp", diff --git a/src/mapleall/maple_ir/include/bin_mpl_export.h b/src/mapleall/maple_ir/include/bin_mpl_export.h index e24638b8be..68258f3b6a 100644 --- a/src/mapleall/maple_ir/include/bin_mpl_export.h +++ b/src/mapleall/maple_ir/include/bin_mpl_export.h @@ -18,6 +18,7 @@ #include "mir_nodes.h" #include "mir_function.h" #include "mir_preg.h" +#include "mir_enumeration.h" #include "parser_opt.h" #include "ea_connection_graph.h" @@ -67,8 +68,8 @@ enum : uint8 { kBinEaCgStart = 41, kBinEaStart = 42, kBinNodeBlock = 43, -// kBinOpStatement : 44, -// kBinOpExpression : 45, + kBinEnumeration = 44, + kBinEnumStart = 45, kBinReturnvals = 46, kBinTypeTabStart = 47, kBinSymStart = 48, @@ -125,6 +126,7 @@ class BinaryMplExport { void OutputPragmaVec(const std::vector &pragmaVec); void OutputClassTypeData(const MIRClassType &type); void OutputSymbol(MIRSymbol *sym); + void OutputEnumeration(MIREnum *mirEnum); void OutputFunction(PUIdx puIdx); void OutputInterfaceTypeData(const MIRInterfaceType &type); void OutputSrcPos(const SrcPosition &pos); @@ -179,6 +181,7 @@ class BinaryMplExport { void DumpBuf(const std::string &name); void AppendAt(const std::string &name, int32 offset); void ExpandFourBuffSize(); + void WriteEnumField(uint64 contentIdx); MIRModule &mod; size_t bufI = 0; diff --git a/src/mapleall/maple_ir/include/bin_mpl_import.h b/src/mapleall/maple_ir/include/bin_mpl_import.h index fe4f53d387..bc3ba10fb8 100644 --- a/src/mapleall/maple_ir/include/bin_mpl_import.h +++ b/src/mapleall/maple_ir/include/bin_mpl_import.h @@ -126,6 +126,7 @@ class BinaryMplImport { void ImportInfoVector(MIRInfoVector &infoVector, MapleVector &infoVectorIsString); void ImportLocalTypeNameTable(MIRTypeNameTable *typeNameTab); void ImportFuncIdInfo(MIRFunction *func); + void ImportEnumeration(); MIRSymbol *ImportLocalSymbol(MIRFunction *func); PregIdx ImportPreg(MIRFunction *func); LabelIdx ImportLabel(MIRFunction *func); @@ -138,6 +139,7 @@ class BinaryMplImport { void ImportReturnValues(MIRFunction *func, CallReturnVector *retv); BlockNode *ImportBlockNode(MIRFunction *func); void ReadFunctionBodyField(); + void ReadEnumField(); void ReadFileAt(const std::string &name, int32 offset); uint8 Read(); int64 ReadInt64(); diff --git a/src/mapleall/maple_ir/include/global_tables.h b/src/mapleall/maple_ir/include/global_tables.h index 492cadf882..0f2078b5ab 100644 --- a/src/mapleall/maple_ir/include/global_tables.h +++ b/src/mapleall/maple_ir/include/global_tables.h @@ -28,6 +28,7 @@ #include "namemangler.h" #include "mir_type.h" #include "mir_const.h" +#include "mir_enumeration.h" namespace maple { using TyIdxFieldAttrPair = std::pair; @@ -838,6 +839,10 @@ class GlobalTables { return *(globalTables.intConstTablePtr); } + static EnumTable &GetEnumTable() { + return globalTables.enumTable; + } + GlobalTables(const GlobalTables &globalTables) = delete; GlobalTables(const GlobalTables &&globalTables) = delete; GlobalTables &operator=(const GlobalTables &globalTables) = delete; @@ -863,6 +868,7 @@ class GlobalTables { StringTable gStringTable; StringTable uStrTable; StringTable u16StringTable; + EnumTable enumTable; }; inline MIRType &GetTypeFromTyIdx(TyIdx idx) { diff --git a/src/mapleall/maple_ir/include/keywords.def b/src/mapleall/maple_ir/include/keywords.def index ad4d20d229..6fe8262fcd 100644 --- a/src/mapleall/maple_ir/include/keywords.def +++ b/src/mapleall/maple_ir/include/keywords.def @@ -32,6 +32,7 @@ KEYWORD(tempvar) KEYWORD(reg) KEYWORD(type) + KEYWORD(enumeration) KEYWORD(func) KEYWORD(struct) KEYWORD(structincomplete) diff --git a/src/mapleall/maple_ir/include/mir_enumeration.h b/src/mapleall/maple_ir/include/mir_enumeration.h new file mode 100644 index 0000000000..bbe8e01a4a --- /dev/null +++ b/src/mapleall/maple_ir/include/mir_enumeration.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) [2022] Futurewei Technologies Co., Ltd. All rights reserved. + * + * OpenArkCompiler is licensed under the Mulan Permissive Software License v2. + * You can use this software according to the terms and conditions of the MulanPSL - 2.0. + * You may obtain a copy of MulanPSL - 2.0 at: + * + * https://opensource.org/licenses/MulanPSL-2.0 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR + * FIT FOR A PARTICULAR PURPOSE. + * See the MulanPSL - 2.0 for more details. + */ +#ifndef MAPLEIR_INCLUDE_MIR_ENUMERATION_H +#define MAPLEIR_INCLUDE_MIR_ENUMERATION_H + +namespace maple { + +using EnumElem = std::pair; +class MIREnum { + public: + explicit MIREnum(PrimType ptyp, GStrIdx stridx): primType(ptyp), nameStrIdx(stridx) {} + ~MIREnum() = default; + + void NewElement(GStrIdx sidx, IntVal value) { + elements.push_back(EnumElem(sidx, value)); + } + void AddNextElement(GStrIdx sidx) { + if (elements.empty()) { + elements.push_back(EnumElem(sidx, IntVal(0, primType))); + return; + } + IntVal newValue = elements.back().second + 1; + elements.push_back(EnumElem(sidx, newValue)); + } + const std::string &GetName() const; + void Dump() const; + + public: + PrimType primType = PTY_i32; // must be integer primtype + GStrIdx nameStrIdx{ 0 }; // name of this enum in global string table + std::vector elements{}; +}; + +struct EnumTable { + std::vector enumTable; + void Dump() { + for (MIREnum *mirEnum : enumTable) { + mirEnum->Dump(); + } + } +}; + +} /* namespace maple */ + +#endif /* MAPLEIR_INCLUDE_MIR_ENUMERATION_H */ diff --git a/src/mapleall/maple_ir/include/mir_parser.h b/src/mapleall/maple_ir/include/mir_parser.h index c68bec1438..3a1305f522 100755 --- a/src/mapleall/maple_ir/include/mir_parser.h +++ b/src/mapleall/maple_ir/include/mir_parser.h @@ -95,6 +95,7 @@ class MIRParser { bool ParseGenericInstantVector(MIRInstantVectorType &insVecType); bool ParseDerivedType(TyIdx &tyIdx, MIRTypeKind kind = kTypeUnknown, const GStrIdx &strIdx = GStrIdx(0)); bool ParseType(TyIdx &tyIdx); + bool ParseEnumeration(); bool ParseStatement(StmtNodePtr &stmt); bool ParseSpecialReg(PregIdx &pRegIdx); bool ParsePseudoReg(PrimType primType, PregIdx &pRegIdx); diff --git a/src/mapleall/maple_ir/src/bin_mpl_export.cpp b/src/mapleall/maple_ir/src/bin_mpl_export.cpp index 674ab98ce5..22693bda06 100644 --- a/src/mapleall/maple_ir/src/bin_mpl_export.cpp +++ b/src/mapleall/maple_ir/src/bin_mpl_export.cpp @@ -1097,6 +1097,27 @@ void BinaryMplExport::WriteEaCgField(EAConnectionGraph *eaCg) { WriteNum(~kBinEaCgStart); } +void BinaryMplExport::WriteEnumField(uint64 contentIdx) { + if (GlobalTables::GetEnumTable().enumTable.empty()) { + return; + } + Fixup(contentIdx, buf.size()); + WriteNum(kBinEnumStart); + uint64 totalSizeIdx = buf.size(); + ExpandFourBuffSize(); // total size of this field to ~BIN_SYM_START + uint64 outEnumSizeIdx = buf.size(); + ExpandFourBuffSize(); // size of OutEnum + int32 size = 0; + for (MIREnum *mirEnum : GlobalTables::GetEnumTable().enumTable) { + OutputEnumeration(mirEnum); + size++; + } + Fixup(totalSizeIdx, buf.size() - totalSizeIdx); + Fixup(outEnumSizeIdx, size); + WriteNum(~kBinEnumStart); + return; +} + void BinaryMplExport::WriteSymField(uint64 contentIdx) { Fixup(contentIdx, buf.size()); WriteNum(kBinSymStart); @@ -1193,7 +1214,7 @@ void BinaryMplExport::WriteContentField4nonJava(int fieldNum, uint64 *fieldStart fieldStartP[kSecondField] = buf.size(); ExpandFourBuffSize(); - WriteNum(kBinTypeStart); + WriteNum(kBinEnumStart); fieldStartP[kThirdField] = buf.size(); ExpandFourBuffSize(); @@ -1228,6 +1249,7 @@ void BinaryMplExport::Export(const std::string &fname, std::unordered_set(mirEnum->primType)); + OutputStr(mirEnum->nameStrIdx); + WriteNum(mirEnum->elements.size()); + for (size_t i = 0; i < mirEnum->elements.size(); ++i) { + OutputStr(mirEnum->elements[i].first); + WriteNum(mirEnum->elements[i].second.GetSXTValue()); + } +} + void UpdateMplt::UpdateCgField(BinaryMplt &binMplt, const CallGraph &cg) const { BinaryMplImport &binImport = binMplt.GetBinImport(); BinaryMplExport &binExport = binMplt.GetBinExport(); diff --git a/src/mapleall/maple_ir/src/bin_mpl_import.cpp b/src/mapleall/maple_ir/src/bin_mpl_import.cpp index 0303b4a4f9..fd67f481f2 100644 --- a/src/mapleall/maple_ir/src/bin_mpl_import.cpp +++ b/src/mapleall/maple_ir/src/bin_mpl_import.cpp @@ -1192,6 +1192,32 @@ void BinaryMplImport::ReadTypeField() { CHECK_FATAL(tag == ~kBinTypeStart, "pattern mismatch in Read TYPE"); } +void BinaryMplImport::ImportEnumeration() { + int64 tag = ReadNum(); + CHECK_FATAL(tag == kBinEnumeration, "expecting kBinEnumeration"); + PrimType ptyp = static_cast(Read()); + GStrIdx gStrIdx = ImportStr(); + MIREnum *mirEnum = new MIREnum(ptyp, gStrIdx); + size_t siz = ReadNum(); + for (size_t i = 0; i < siz; i++) { + gStrIdx = ImportStr(); + IntVal intVal(ReadNum(), ptyp); + mirEnum->NewElement(gStrIdx, intVal); + } + GlobalTables::GetEnumTable().enumTable.push_back(mirEnum); +} + +void BinaryMplImport::ReadEnumField() { + SkipTotalSize(); + + int32 size = ReadInt(); + for (int64 i = 0; i < size; ++i) { + (void)ImportEnumeration(); + } + int64 tag = ReadNum(); + CHECK_FATAL(tag == ~kBinEnumStart, "pattern mismatch in Reading ENUM"); +} + CallInfo *BinaryMplImport::ImportCallInfo() { int64 tag = ReadNum(); if (tag < 0) { @@ -1658,6 +1684,10 @@ bool BinaryMplImport::Import(const std::string &fname, bool readSymbols, bool re ReadFunctionBodyField(); break; } + case kBinEnumStart: { + ReadEnumField(); + break; + } default: CHECK_FATAL(false, "should not run here"); } diff --git a/src/mapleall/maple_ir/src/mir_enumeration.cpp b/src/mapleall/maple_ir/src/mir_enumeration.cpp new file mode 100644 index 0000000000..9ee9c9194e --- /dev/null +++ b/src/mapleall/maple_ir/src/mir_enumeration.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) [2022] Futurewei Technologies Co., Ltd. All rights reserved. + * + * OpenArkCompiler is licensed under the Mulan Permissive Software License v2. + * You can use this software according to the terms and conditions of the MulanPSL - 2.0. + * You may obtain a copy of MulanPSL - 2.0 at: + * + * https://opensource.org/licenses/MulanPSL-2.0 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR + * FIT FOR A PARTICULAR PURPOSE. + * See the MulanPSL - 2.0 for more details. + */ + +#include "global_tables.h" +#include "printing.h" +#include "mir_enumeration.h" + +// The syntax of enumerated type in Maple IR is: +// enumeration { = , ... } +// If '=' is not specified, will adopt the last value plus 1. +// The default starting is 0. + +namespace maple { + +const std::string &MIREnum::GetName() const { + return GlobalTables::GetStrTable().GetStringFromStrIdx(nameStrIdx); +} + +void MIREnum::Dump() const { + LogInfo::MapleLogger() << "enumeration $" << GetName() << " " << GetPrimTypeName(primType) << " {"; + IntVal lastValue(elements.front().second-9, primType); // so as to fail first check + for (size_t i = 0; i < elements.size(); i++) { + EnumElem curElem = elements[i]; + LogInfo::MapleLogger() << " $" << GlobalTables::GetStrTable().GetStringFromStrIdx(curElem.first); + if (curElem.second != lastValue+1) { + LogInfo::MapleLogger() << " = " << curElem.second; + } + lastValue = curElem.second; + if (i+1 != elements.size()) { + LogInfo::MapleLogger() << ","; + } + } + LogInfo::MapleLogger() << " }\n"; +} + +} // namespace maple diff --git a/src/mapleall/maple_ir/src/mir_module.cpp b/src/mapleall/maple_ir/src/mir_module.cpp index 1b0771c0dd..d028ea8fda 100644 --- a/src/mapleall/maple_ir/src/mir_module.cpp +++ b/src/mapleall/maple_ir/src/mir_module.cpp @@ -231,6 +231,7 @@ void MIRModule::DumpGlobals(bool emitStructureType) const { } LogInfo::MapleLogger() << std::dec; } + GlobalTables::GetEnumTable().Dump(); if (flavor < kMmpl || flavor == kFlavorLmbc) { for (auto it = typeDefOrder.begin(); it != typeDefOrder.end(); ++it) { TyIdx tyIdx = typeNameTab->GetTyIdxFromGStrIdx(*it); diff --git a/src/mapleall/maple_ir/src/parser.cpp b/src/mapleall/maple_ir/src/parser.cpp index 088ab64858..dc40f9b3de 100644 --- a/src/mapleall/maple_ir/src/parser.cpp +++ b/src/mapleall/maple_ir/src/parser.cpp @@ -27,6 +27,7 @@ #include "clone.h" #include "string_utils.h" #include "debug_info.h" +#include "mir_enumeration.h" namespace { using namespace maple; @@ -1684,6 +1685,81 @@ bool MIRParser::ParseTypedef() { return true; } +bool MIRParser::ParseEnumeration() { + if (lexer.GetTokenKind() != TK_enumeration) { + Error("expect enum but get "); + return false; + } + TokenKind tokenKind = lexer.NextToken(); + if (tokenKind != TK_gname) { + Error("expect global type name but get "); + return false; + } + const std::string &name = lexer.GetName(); + GStrIdx strIdx = GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(name); + lexer.NextToken(); + TyIdx tyIdx(0); + if (!ParsePrimType(tyIdx)) { + Error("expect primitive type name but get "); + return false; + } + PrimType ptyp = GlobalTables::GetTypeTable().GetTypeFromTyIdx(tyIdx)->GetPrimType(); + if (!IsPrimitiveInteger(ptyp)) { + Error("base type of enum cannot be non-integer"); + return false; + } + if (lexer.GetTokenKind() != TK_lbrace) { + Error("expect left brace but get "); + return false; + } + tokenKind = lexer.NextToken(); + if (tokenKind != TK_gname) { + Error("expect global enum value name but get "); + return false; + } + MIREnum *mirEnum = new MIREnum(ptyp, strIdx); + strIdx = GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(name); + tokenKind = lexer.NextToken(); + if (tokenKind == TK_eqsign) { + tokenKind = lexer.NextToken(); + if (tokenKind != TK_intconst) { + Error("expect integer constant but get "); + return false; + } + mirEnum->NewElement(strIdx, IntVal(lexer.GetTheIntVal(), ptyp)); + tokenKind = lexer.NextToken(); + } else { + mirEnum->AddNextElement(strIdx); + } + while (tokenKind == TK_coma) { + tokenKind = lexer.NextToken(); + if (tokenKind != TK_gname) { + Error("expect global enum value name but get "); + return false; + } + strIdx = GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(name); + tokenKind = lexer.NextToken(); + if (tokenKind == TK_eqsign) { + tokenKind = lexer.NextToken(); + if (tokenKind != TK_intconst) { + Error("expect integer constant but get "); + return false; + } + mirEnum->NewElement(strIdx, IntVal(lexer.GetTheIntVal(), ptyp)); + tokenKind = lexer.NextToken(); + } else { + mirEnum->AddNextElement(strIdx); + } + } + if (tokenKind != TK_rbrace) { + Error("expect right brace but get "); + return false; + } + lexer.NextToken(); + GlobalTables::GetEnumTable().enumTable.push_back(mirEnum); + return true; +} + bool MIRParser::ParseJavaClassInterface(MIRSymbol &symbol, bool isClass) { TokenKind tk = lexer.NextToken(); if (tk != TK_gname) { @@ -2734,6 +2810,7 @@ std::map MIRParser::InitFuncPtrMap funcPtrMap[TK_javaclass] = &MIRParser::ParseMIRForClass; funcPtrMap[TK_javainterface] = &MIRParser::ParseMIRForInterface; funcPtrMap[TK_type] = &MIRParser::ParseTypedef; + funcPtrMap[TK_enumeration] = &MIRParser::ParseEnumeration; funcPtrMap[TK_flavor] = &MIRParser::ParseMIRForFlavor; funcPtrMap[TK_srclang] = &MIRParser::ParseMIRForSrcLang; funcPtrMap[TK_globalmemsize] = &MIRParser::ParseMIRForGlobalMemSize; -- Gitee From 454edfa1e9363ce3191f548aaa9a530a749fc8de Mon Sep 17 00:00:00 2001 From: Fred Chow Date: Tue, 19 Jul 2022 11:05:49 -0700 Subject: [PATCH 2/2] Updated to latest version of the license --- src/mapleall/maple_ir/include/mir_enumeration.h | 13 +++++++------ src/mapleall/maple_ir/src/mir_enumeration.cpp | 12 ++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mapleall/maple_ir/include/mir_enumeration.h b/src/mapleall/maple_ir/include/mir_enumeration.h index bbe8e01a4a..8c90f960bc 100644 --- a/src/mapleall/maple_ir/include/mir_enumeration.h +++ b/src/mapleall/maple_ir/include/mir_enumeration.h @@ -1,17 +1,18 @@ /* - * Copyright (c) [2022] Futurewei Technologies Co., Ltd. All rights reserved. + * Copyright (c) [2022] Futurewei Technologies, Inc. All rights reserved. * - * OpenArkCompiler is licensed under the Mulan Permissive Software License v2. - * You can use this software according to the terms and conditions of the MulanPSL - 2.0. - * You may obtain a copy of MulanPSL - 2.0 at: + * OpenArkCompiler is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: * - * https://opensource.org/licenses/MulanPSL-2.0 + * http://license.coscl.org.cn/MulanPSL2 * * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR * FIT FOR A PARTICULAR PURPOSE. - * See the MulanPSL - 2.0 for more details. + * See the Mulan PSL v2 for more details. */ + #ifndef MAPLEIR_INCLUDE_MIR_ENUMERATION_H #define MAPLEIR_INCLUDE_MIR_ENUMERATION_H diff --git a/src/mapleall/maple_ir/src/mir_enumeration.cpp b/src/mapleall/maple_ir/src/mir_enumeration.cpp index 9ee9c9194e..be26f3bc9f 100644 --- a/src/mapleall/maple_ir/src/mir_enumeration.cpp +++ b/src/mapleall/maple_ir/src/mir_enumeration.cpp @@ -1,16 +1,16 @@ /* - * Copyright (c) [2022] Futurewei Technologies Co., Ltd. All rights reserved. + * Copyright (c) [2022] Futurewei Technologies, Inc. All rights reserved. * - * OpenArkCompiler is licensed under the Mulan Permissive Software License v2. - * You can use this software according to the terms and conditions of the MulanPSL - 2.0. - * You may obtain a copy of MulanPSL - 2.0 at: + * OpenArkCompiler is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: * - * https://opensource.org/licenses/MulanPSL-2.0 + * http://license.coscl.org.cn/MulanPSL2 * * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR * FIT FOR A PARTICULAR PURPOSE. - * See the MulanPSL - 2.0 for more details. + * See the Mulan PSL v2 for more details. */ #include "global_tables.h" -- Gitee