1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #include "util/logger.h" 17 18 #include "codegen/rust_code_emitter.h" 19 20 #include "securec.h" 21 #include "util/file.h" 22 23 namespace OHOS { 24 namespace Idl { 25 namespace { 26 const uint32_t WRAP_ANCHOR = 4; 27 } 28 EmitInterface()29 void RustCodeEmitter::EmitInterface() 30 { 31 String filePath = String::Format("%s/%s.rs", directory_.string(), interfaceName_.string()); 32 File file(filePath, File::WRITE); 33 StringBuilder sb; 34 EmitInterface(sb); 35 String data = sb.ToString(); 36 file.WriteData(data.string(), data.GetLength()); 37 file.Flush(); 38 file.Close(); 39 } 40 EmitInterfaceProxy()41 void RustCodeEmitter::EmitInterfaceProxy() 42 { 43 return; 44 } 45 EmitInterfaceStub()46 void RustCodeEmitter::EmitInterfaceStub() 47 { 48 return; 49 } 50 EmitInterface(StringBuilder & sb)51 void RustCodeEmitter::EmitInterface(StringBuilder& sb) 52 { 53 if (metaInterface_->license_) { 54 EmitLicense(sb); 55 sb.Append("\n"); 56 } 57 EmitMacros(sb); 58 EmitHeaders(sb); 59 sb.Append("\n"); 60 EmitCommands(sb); 61 sb.Append("\n"); 62 EmitRemoteObject(sb); 63 sb.Append("\n"); 64 EmitBrokers(sb); 65 sb.Append("\n"); 66 EmitRemoteRequest(sb); 67 sb.Append("\n"); 68 EmitStub(sb); 69 sb.Append("\n"); 70 EmitProxy(sb); 71 } 72 EmitLicense(StringBuilder & sb)73 void RustCodeEmitter::EmitLicense(StringBuilder& sb) 74 { 75 sb.Append(metaInterface_->license_).Append("\n"); 76 } 77 EmitMacros(StringBuilder & sb)78 void RustCodeEmitter::EmitMacros(StringBuilder& sb) 79 { 80 sb.Append("#![allow(missing_docs)]\n"); 81 sb.Append("#![allow(unused_variables)]\n"); 82 sb.Append("#![allow(unused_mut)]\n"); 83 sb.Append("\n"); 84 } EmitHeaders(StringBuilder & sb)85 void RustCodeEmitter::EmitHeaders(StringBuilder& sb) 86 { 87 EmitCommonHeaders(sb); 88 EmitIPCHeaders(sb); 89 if (EmitCustomHeaders(sb)) { 90 sb.Append("\n"); 91 } 92 } 93 EmitIPCHeaders(StringBuilder & sb)94 void RustCodeEmitter::EmitIPCHeaders(StringBuilder& sb) 95 { 96 sb.Append("extern crate ipc_rust;\n"); 97 sb.Append("\n"); 98 sb.Append("use ipc_rust::{\n"); 99 sb.Append(" IRemoteBroker, IRemoteObj, RemoteStub, Result,\n"); 100 sb.Append(" RemoteObj, define_remote_object, FIRST_CALL_TRANSACTION\n"); 101 sb.Append("};\n"); 102 sb.Append("use ipc_rust::{MsgParcel, BorrowedMsgParcel};\n"); 103 sb.Append("\n"); 104 } 105 EmitCommonHeaders(StringBuilder & sb)106 void RustCodeEmitter::EmitCommonHeaders(StringBuilder& sb) 107 { 108 bool useMap = false; 109 for (int i = 0; i < metaComponent_->typeNumber_; i++) { 110 MetaType* mt = metaComponent_->types_[i]; 111 switch (mt->kind_) { 112 case TypeKind::Map: { 113 if (!useMap) { 114 sb.Append("use std::collections::HashMap;\n"); 115 useMap = true; 116 } 117 break; 118 } 119 120 default: 121 break; 122 } 123 } 124 if (useMap) { 125 sb.Append("\n"); 126 } 127 } 128 TrimDot(const String & fpnp)129 String RustCodeEmitter::TrimDot(const String& fpnp) 130 { 131 if (fpnp.IsEmpty()) { 132 return nullptr; 133 } 134 135 int left = 0; 136 int right = fpnp.GetLength() - 1; 137 while (fpnp[left] == ' ' || fpnp[left] == '.') { 138 left++; 139 } 140 141 while (fpnp[right] == ' ' || fpnp[right] == '.') { 142 right--; 143 } 144 145 if (left >= right) { 146 return nullptr; 147 } 148 149 return fpnp.Substring(left, right + 1); 150 } 151 GeneratePath(const String & fpnp)152 String RustCodeEmitter::GeneratePath(const String& fpnp) 153 { 154 int pos = fpnp.IndexOf(".."); 155 if (pos == -1) { 156 String path = TrimDot(fpnp); 157 if (path.IsEmpty()) { 158 return nullptr; 159 } 160 return path.Replace(".", "::"); 161 } 162 163 String path = TrimDot(fpnp.Substring(0, pos + 1)); 164 String file = TrimDot(fpnp.Substring(pos)); 165 if (path.IsEmpty()) { 166 return nullptr; 167 } 168 169 if (path.IndexOf("..") != -1 || file.IndexOf("..") != -1) { 170 return nullptr; 171 } 172 173 StringBuilder realPath; 174 realPath.Append(path.Replace(".", "::")).Append("::{"); 175 realPath.Append(file.Replace(".", ", ")); 176 realPath.Append("}"); 177 178 return realPath.ToString(); 179 } 180 AppendRealPath(StringBuilder & sb,const String & fpnpp)181 bool RustCodeEmitter::AppendRealPath(StringBuilder& sb, const String& fpnpp) 182 { 183 String result = GeneratePath(fpnpp); 184 if (result.IsEmpty()) { 185 return false; 186 } 187 sb.Append("use ").Append(result).Append(";\n"); 188 return true; 189 } 190 EmitCustomHeaders(StringBuilder & sb)191 bool RustCodeEmitter::EmitCustomHeaders(StringBuilder& sb) 192 { 193 uint32_t custom = false; 194 for (int i = 0; i < metaComponent_->sequenceableNumber_; i++) { 195 MetaSequenceable* ms = metaComponent_->sequenceables_[i]; 196 bool addPathMsRes = AppendRealPath(sb, String(ms->namespace_) + String(ms->name_)); 197 custom |= static_cast<uint32_t>(addPathMsRes); 198 } 199 200 for (int i = 0; i < metaComponent_->interfaceNumber_; i++) { 201 MetaInterface* mi = metaComponent_->interfaces_[i]; 202 if (mi->external_) { 203 bool addPathMiRes = AppendRealPath(sb, String(mi->namespace_) + String(mi->name_)); 204 custom |= static_cast<uint32_t>(addPathMiRes); 205 } 206 } 207 return static_cast<bool>(custom); 208 } 209 EmitCommands(StringBuilder & sb)210 void RustCodeEmitter::EmitCommands(StringBuilder& sb) 211 { 212 EmitCommandEnums(sb); 213 } 214 AppendCommandEnums(StringBuilder & sb)215 void RustCodeEmitter::AppendCommandEnums(StringBuilder& sb) 216 { 217 if (metaInterface_->methodNumber_ > 0) { 218 sb.AppendFormat(" %s = FIRST_CALL_TRANSACTION,\n", 219 GetCodeFromMethod(metaInterface_->methods_[0]->name_).string()); 220 } 221 222 for (int i = 1; i < metaInterface_->methodNumber_; i++) { 223 MetaMethod* mm = metaInterface_->methods_[i]; 224 sb.AppendFormat(" %s,\n", GetCodeFromMethod(mm->name_).string(), i); 225 } 226 } 227 GetCodeFromMethod(const char * name)228 String RustCodeEmitter::GetCodeFromMethod(const char* name) 229 { 230 StringBuilder sb; 231 sb.Append("Code"); 232 const char* p = name; 233 bool hasUpper = false; 234 while (p != nullptr && *p != '\0') { 235 if (*p != '_') { 236 if (!hasUpper) { 237 sb.Append(toupper(*p)); 238 hasUpper = true; 239 } else { 240 sb.Append(*p); 241 } 242 } else { 243 hasUpper = false; 244 } 245 p++; 246 } 247 return sb.ToString(); 248 } 249 GetNameFromParameter(const char * name)250 String RustCodeEmitter::GetNameFromParameter(const char* name) 251 { 252 StringBuilder sb; 253 const char* p = name; 254 bool start = true; 255 while (p != nullptr && *p != '\0') { 256 if (start) { 257 if (isupper(*p)) { 258 sb.Append('p'); 259 } 260 start = false; 261 } 262 263 if (isupper(*p)) { 264 sb.Append('_'); 265 sb.Append(tolower(*p)); 266 } else { 267 sb.Append(*p); 268 } 269 p++; 270 } 271 return sb.ToString(); 272 } 273 EmitCommandEnums(StringBuilder & sb)274 void RustCodeEmitter::EmitCommandEnums(StringBuilder& sb) 275 { 276 sb.AppendFormat("pub enum %sCode {\n", interfaceName_.string()); 277 AppendCommandEnums(sb); 278 sb.Append("}\n"); 279 } 280 EmitRemoteObject(StringBuilder & sb)281 void RustCodeEmitter::EmitRemoteObject(StringBuilder& sb) 282 { 283 sb.Append("define_remote_object!(\n"); 284 if (interfaceFullName_.StartsWith(".")) { 285 sb.AppendFormat(" %s[\"%s\"] {\n", interfaceName_.string(), interfaceName_.string()); 286 } else { 287 sb.AppendFormat(" %s[\"%s\"] {\n", interfaceName_.string(), interfaceFullName_.string()); 288 } 289 sb.AppendFormat(" stub: %s(on_remote_request),\n", stubName_.string()); 290 sb.AppendFormat(" proxy: %s,\n", proxyName_.string()); 291 sb.Append(" }\n"); 292 sb.Append(");\n"); 293 } 294 EmitBrokers(StringBuilder & sb)295 void RustCodeEmitter::EmitBrokers(StringBuilder& sb) 296 { 297 sb.AppendFormat("pub trait %s: IRemoteBroker {\n", interfaceName_.string()); 298 AppendBrokerMethods(sb); 299 sb.Append("}\n"); 300 } 301 WrapLine(StringBuilder & sb,int index,const String & prefix)302 void RustCodeEmitter::WrapLine(StringBuilder& sb, int index, const String& prefix) 303 { 304 if ((index + 1) % WRAP_ANCHOR == 0) { 305 sb.AppendFormat(",\n%s", prefix.string()); 306 } else { 307 sb.Append(", "); 308 } 309 } 310 AppendBrokerMethods(StringBuilder & sb)311 void RustCodeEmitter::AppendBrokerMethods(StringBuilder& sb) 312 { 313 for (int i = 0; i < metaInterface_->methodNumber_; i++) { 314 MetaMethod* mm = metaInterface_->methods_[i]; 315 sb.AppendFormat(" fn %s(&self", mm->name_); 316 for (int i = 0; i < mm->parameterNumber_; i++) { 317 WrapLine(sb, i, " "); 318 AppendBrokerParameters(sb, mm->parameters_[i]); 319 } 320 sb.AppendFormat(") -> Result<%s>;\n", ConvertType(metaComponent_->types_[mm->returnTypeIndex_]).string()); 321 } 322 } 323 AppendBrokerParameters(StringBuilder & sb,MetaParameter * mp)324 void RustCodeEmitter::AppendBrokerParameters(StringBuilder& sb, MetaParameter* mp) 325 { 326 sb.AppendFormat("%s: &%s", 327 GetNameFromParameter(mp->name_).string(), ConvertType(metaComponent_->types_[mp->typeIndex_], true).string()); 328 } 329 ConvertType(MetaType * mt,bool pt)330 String RustCodeEmitter::ConvertType(MetaType* mt, bool pt) 331 { 332 switch (mt->kind_) { 333 case TypeKind::Unknown: 334 case TypeKind::Void: 335 return "()"; 336 case TypeKind::Char: 337 return "char"; 338 case TypeKind::Boolean: 339 return "bool"; 340 case TypeKind::Byte: 341 return "i8"; 342 case TypeKind::Short: 343 return "i16"; 344 case TypeKind::Integer: 345 return "i32"; 346 case TypeKind::Long: 347 return "i64"; 348 case TypeKind::Float: 349 return "f32"; 350 case TypeKind::Double: 351 return "f64"; 352 case TypeKind::String: 353 return pt ? "str" : "String"; 354 case TypeKind::Sequenceable: 355 return metaComponent_->sequenceables_[mt->index_]->name_; 356 case TypeKind::Interface: 357 return metaComponent_->interfaces_[mt->index_]->name_; 358 case TypeKind::Map: 359 return String::Format("HashMap<%s, %s>", 360 ConvertType(metaComponent_->types_[mt->nestedTypeIndexes_[0]]).string(), 361 ConvertType(metaComponent_->types_[mt->nestedTypeIndexes_[1]]).string()); 362 case TypeKind::List: 363 case TypeKind::Array: 364 return String::Format((pt ? "[%s]" : "Vec<%s>"), 365 ConvertType(metaComponent_->types_[mt->nestedTypeIndexes_[0]]).string()); 366 default: 367 return "()"; 368 } 369 } 370 EmitRemoteRequest(StringBuilder & sb)371 void RustCodeEmitter::EmitRemoteRequest(StringBuilder& sb) 372 { 373 sb.AppendFormat("fn on_remote_request(stub: &dyn %s, code: u32, data: &BorrowedMsgParcel,\n", 374 interfaceName_.string()); 375 sb.Append(" reply: &mut BorrowedMsgParcel) -> Result<()> {\n"); 376 sb.Append(" match code {\n"); 377 AddRemoteRequestMethods(sb); 378 sb.Append(" _ => Err(-1)\n"); 379 sb.Append(" }\n"); 380 sb.Append("}\n"); 381 } 382 AddRemoteRequestParameters(StringBuilder & sb,MetaMethod * mm)383 void RustCodeEmitter::AddRemoteRequestParameters(StringBuilder& sb, MetaMethod* mm) 384 { 385 for (int i = 0; i < mm->parameterNumber_; i++) { 386 MetaParameter* mp = mm->parameters_[i]; 387 sb.AppendFormat("&%s", GetNameFromParameter(mp->name_).string()); 388 if (i + 1 != mm->parameterNumber_) { 389 WrapLine(sb, i, " "); 390 } 391 } 392 } 393 ReadListFromParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)394 void RustCodeEmitter::ReadListFromParcel(StringBuilder& sb, MetaType* mt, const String& result, 395 const String& name, const String& prefix) 396 { 397 sb.Append(prefix).AppendFormat("let %s : %s = %s.read()?;\n", 398 name.string(), ConvertType(mt).string(), result.string()); 399 } 400 ReadMapFromParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)401 void RustCodeEmitter::ReadMapFromParcel(StringBuilder& sb, MetaType* mt, const String& result, 402 const String& name, const String& prefix) 403 { 404 sb.Append(prefix).AppendFormat("let mut %s = HashMap::new();\n", name.string()); 405 sb.Append(prefix).AppendFormat("let len = %s.read()?;\n", result.string()); 406 sb.Append(prefix).Append("for i in 0..len {\n"); 407 StringBuilder k; 408 StringBuilder v; 409 k.Append(name).Append("k"); 410 v.Append(name).Append("v"); 411 ReadFromParcel(sb, metaComponent_->types_[mt->nestedTypeIndexes_[0]], 412 result, k.ToString().string(), prefix + " "); 413 ReadFromParcel(sb, metaComponent_->types_[mt->nestedTypeIndexes_[1]], 414 result, v.ToString().string(), prefix + " "); 415 sb.Append(prefix + " ").AppendFormat("%s.insert(%s, %s);\n", 416 name.string(), k.ToString().string(), v.ToString().string()); 417 sb.Append(prefix).Append("}\n"); 418 } 419 ReadFromParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)420 void RustCodeEmitter::ReadFromParcel(StringBuilder& sb, MetaType* mt, const String& result, 421 const String& name, const String& prefix) 422 { 423 if (mt->kind_ == TypeKind::Map) { 424 ReadMapFromParcel(sb, mt, result, name, prefix); 425 } else if (mt->kind_ == TypeKind::List || mt->kind_ == TypeKind::Array) { 426 ReadListFromParcel(sb, mt, result, name, prefix); 427 } else { 428 sb.Append(prefix).AppendFormat("let %s : %s = %s.read()?;\n", 429 name.string(), ConvertType(mt).string(), result.string()); 430 } 431 } 432 WriteListToParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)433 void RustCodeEmitter::WriteListToParcel(StringBuilder& sb, MetaType* mt, const String& result, 434 const String& name, const String& prefix) 435 { 436 sb.Append(prefix).AppendFormat("%s.write(&%s)?;\n", result.string(), name.string()); 437 } 438 WriteMapToParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)439 void RustCodeEmitter::WriteMapToParcel(StringBuilder& sb, MetaType* mt, const String& result, 440 const String& name, const String& prefix) 441 { 442 sb.Append(prefix).AppendFormat("%s.write(&(%s.len() as u32))?;\n", result.string(), name.string()); 443 sb.Append(prefix).AppendFormat("for (key, value) in %s.iter() {\n", name.string()); 444 WriteToParcel(sb, metaComponent_->types_[mt->nestedTypeIndexes_[0]], result, "key", prefix + " "); 445 WriteToParcel(sb, metaComponent_->types_[mt->nestedTypeIndexes_[1]], result, "value", prefix + " "); 446 sb.Append(prefix).Append("}\n"); 447 } 448 WriteToParcel(StringBuilder & sb,MetaType * mt,const String & result,const String & name,const String & prefix)449 void RustCodeEmitter::WriteToParcel(StringBuilder& sb, MetaType* mt, const String& result, 450 const String& name, const String& prefix) 451 { 452 if (mt->kind_ == TypeKind::Map) { 453 WriteMapToParcel(sb, mt, result, name, prefix); 454 } else if (mt->kind_ == TypeKind::List || mt->kind_ == TypeKind::Array) { 455 WriteListToParcel(sb, mt, result, name, prefix); 456 } else { 457 sb.Append(prefix).AppendFormat("%s.write(&%s)?;\n", result.string(), name.string()); 458 } 459 } 460 AddRemoteRequestMethods(StringBuilder & sb)461 void RustCodeEmitter::AddRemoteRequestMethods(StringBuilder& sb) 462 { 463 for (int i = 0; i < metaInterface_->methodNumber_; i++) { 464 MetaMethod* mm = metaInterface_->methods_[i]; 465 sb.AppendFormat(" %d => {\n", i + 1); 466 for (int j = 0; j < mm->parameterNumber_; j++) { 467 ReadFromParcel(sb, metaComponent_->types_[mm->parameters_[j]->typeIndex_], "data", 468 GetNameFromParameter(mm->parameters_[j]->name_), " "); 469 } 470 MetaType* mt = metaComponent_->types_[mm->returnTypeIndex_]; 471 if (mt->kind_ != TypeKind::Unknown && mt->kind_ != TypeKind::Void) { 472 sb.AppendFormat(" let result = stub.%s(", mm->name_); 473 } else { 474 sb.AppendFormat(" stub.%s(", mm->name_); 475 } 476 AddRemoteRequestParameters(sb, mm); 477 sb.Append(")?;\n"); 478 if (mt->kind_ != TypeKind::Unknown && mt->kind_ != TypeKind::Void) { 479 WriteToParcel(sb, mt, "reply", "result", " "); 480 } 481 sb.Append(" Ok(())\n"); 482 sb.Append(" }\n"); 483 } 484 } 485 EmitStub(StringBuilder & sb)486 void RustCodeEmitter::EmitStub(StringBuilder& sb) 487 { 488 sb.AppendFormat("impl %s for RemoteStub<%s> {\n", interfaceName_.string(), stubName_.string()); 489 AppendStubMethods(sb); 490 sb.Append("}\n"); 491 } 492 AppendStubParameters(StringBuilder & sb,MetaMethod * mm)493 void RustCodeEmitter::AppendStubParameters(StringBuilder& sb, MetaMethod* mm) 494 { 495 for (int i = 0; i < mm->parameterNumber_; i++) { 496 sb.Append(GetNameFromParameter(mm->parameters_[i]->name_)); 497 if (i + 1 != mm->parameterNumber_) { 498 WrapLine(sb, i, " "); 499 } 500 } 501 } 502 AppendStubMethods(StringBuilder & sb)503 void RustCodeEmitter::AppendStubMethods(StringBuilder& sb) 504 { 505 for (int i = 0; i < metaInterface_->methodNumber_; i++) { 506 MetaMethod* mm = metaInterface_->methods_[i]; 507 sb.AppendFormat(" fn %s(&self", mm->name_); 508 for (int i = 0; i < mm->parameterNumber_; i++) { 509 WrapLine(sb, i, " "); 510 AppendBrokerParameters(sb, mm->parameters_[i]); 511 } 512 513 sb.AppendFormat(") -> Result<%s> {\n", 514 ConvertType(metaComponent_->types_[mm->returnTypeIndex_]).string()); 515 sb.AppendFormat(" self.0.%s(", mm->name_); 516 AppendStubParameters(sb, mm); 517 sb.Append(")\n"); 518 sb.Append(" }\n"); 519 if (i != metaInterface_->methodNumber_ - 1) { 520 sb.Append("\n"); 521 } 522 } 523 } 524 EmitProxy(StringBuilder & sb)525 void RustCodeEmitter::EmitProxy(StringBuilder& sb) 526 { 527 sb.AppendFormat("impl %s for %s {\n", interfaceName_.string(), proxyName_.string()); 528 AppendProxyMethods(sb); 529 sb.Append("}\n"); 530 } 531 AppendProxyMethods(StringBuilder & sb)532 void RustCodeEmitter::AppendProxyMethods(StringBuilder& sb) 533 { 534 for (int i = 0; i < metaInterface_->methodNumber_; i++) { 535 MetaMethod* mm = metaInterface_->methods_[i]; 536 sb.AppendFormat(" fn %s(&self", mm->name_); 537 for (int i = 0; i < mm->parameterNumber_; i++) { 538 WrapLine(sb, i, " "); 539 AppendBrokerParameters(sb, mm->parameters_[i]); 540 } 541 sb.AppendFormat(") -> Result<%s> {\n", 542 ConvertType(metaComponent_->types_[mm->returnTypeIndex_]).string()); 543 sb.Append(" let mut data = MsgParcel::new().expect(\"MsgParcel should success\");\n"); 544 for (int j = 0; j < mm->parameterNumber_; j++) { 545 WriteToParcel(sb, metaComponent_->types_[mm->parameters_[j]->typeIndex_], "data", 546 GetNameFromParameter(mm->parameters_[j]->name_), " "); 547 } 548 MetaType* mt = metaComponent_->types_[mm->returnTypeIndex_]; 549 if (mt->kind_ == TypeKind::Unknown || mt->kind_ == TypeKind::Void) { 550 sb.AppendFormat(" let _reply = self.remote.send_request(%sCode", interfaceName_.string()); 551 } else { 552 sb.AppendFormat(" let reply = self.remote.send_request(%sCode", interfaceName_.string()); 553 } 554 sb.AppendFormat("::%s as u32, &data, ", GetCodeFromMethod(mm->name_).string()); 555 if ((mm->properties_ & METHOD_PROPERTY_ONEWAY) != 0) { 556 sb.Append("true"); 557 } else { 558 sb.Append("false"); 559 } 560 sb.Append(")?;\n"); 561 if (mt->kind_ == TypeKind::Unknown || mt->kind_ == TypeKind::Void) { 562 sb.Append(" ").Append("Ok(())\n"); 563 } else { 564 ReadFromParcel(sb, mt, "reply", "result", " "); 565 sb.Append(" ").Append("Ok(result)\n"); 566 } 567 sb.Append(" }\n"); 568 569 if (i != metaInterface_->methodNumber_ - 1) { 570 sb.Append("\n"); 571 } 572 } 573 } 574 } 575 } 576