slang_rs.cpp revision 2e35b136cc2434080fcd682d2f95e53a87675dd4
1edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/* 2edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Copyright 2010, The Android Open Source Project 3edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 4edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * you may not use this file except in compliance with the License. 6edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * You may obtain a copy of the License at 7edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 8edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 10edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * See the License for the specific language governing permissions and 14edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * limitations under the License. 15edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project */ 16edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 17edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "slang_rs.h" 18edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 19edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <cstring> 20edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <list> 21edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <sstream> 22edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <string> 23edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <utility> 24edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include <vector> 25edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 26edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "clang/Basic/SourceLocation.h" 27edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 28edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "clang/Frontend/FrontendDiagnostic.h" 29edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 30edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "clang/Sema/SemaDiagnostic.h" 31edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 32edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "llvm/Support/Path.h" 33edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 34edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "os_sep.h" 35edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "slang_rs_backend.h" 36edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "slang_rs_context.h" 37edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#include "slang_rs_export_type.h" 38edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 39edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectnamespace slang { 40edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 41edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#define RS_HEADER_SUFFIX "rsh" 42edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 43edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/* RS_HEADER_ENTRY(name, default_included) */ 44edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project#define ENUM_RS_HEADER() \ 45edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_types, 1) \ 46edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_cl, 1) \ 47edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_core, 1) \ 48edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_math, 1) \ 49edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_time, 1) \ 50edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RS_HEADER_ENTRY(rs_graphics, 0) 51edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 52edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectbool SlangRS::reflectToJava(const std::string &OutputPathBase, 53edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project const std::string &OutputPackageName, 54edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project std::string *RealPackageName) { 55edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project return mRSContext->reflectToJava(OutputPathBase, 56edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project OutputPackageName, 57edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project getInputFileName(), 58edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project getOutputFileName(), 59edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RealPackageName); 60edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project} 61edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 62edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectbool SlangRS::generateBitcodeAccessor(const std::string &OutputPathBase, 63edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project const std::string &PackageName) { 64edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RSSlangReflectUtils::BitCodeAccessorContext BCAccessorContext; 65edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 66edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BCAccessorContext.rsFileName = getInputFileName().c_str(); 67edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BCAccessorContext.bcFileName = getOutputFileName().c_str(); 68edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BCAccessorContext.reflectPath = OutputPathBase.c_str(); 69edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BCAccessorContext.packageName = PackageName.c_str(); 70edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BCAccessorContext.bcStorage = BCST_JAVA_CODE; // Must be BCST_JAVA_CODE 71edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 72edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project return RSSlangReflectUtils::GenerateBitCodeAccessor(BCAccessorContext); 73edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project} 74edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 75edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectbool SlangRS::checkODR(const char *CurInputFile) { 76edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project for (RSContext::ExportableList::iterator I = mRSContext->exportable_begin(), 77edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project E = mRSContext->exportable_end(); 78edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project I != E; 79edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project I++) { 80edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RSExportable *RSE = *I; 81edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (RSE->getKind() != RSExportable::EX_TYPE) 82edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project continue; 83edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 84edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RSExportType *ET = static_cast<RSExportType *>(RSE); 85edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (ET->getClass() != RSExportType::ExportClassRecord) 86edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project continue; 87edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 88edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RSExportRecordType *ERT = static_cast<RSExportRecordType *>(ET); 89edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 90edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Artificial record types (create by us not by user in the source) always 91edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // conforms the ODR. 92edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (ERT->isArtificial()) 93edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project continue; 94edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 95edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Key to lookup ERT in ReflectedDefinitions 96edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project llvm::StringRef RDKey(ERT->getName()); 97edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project ReflectedDefinitionListTy::const_iterator RD = 98edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project ReflectedDefinitions.find(RDKey); 99edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 100edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (RD != ReflectedDefinitions.end()) { 101edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project const RSExportRecordType *Reflected = RD->getValue().first; 102edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // There's a record (struct) with the same name reflected before. Enforce 103edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // ODR checking - the Reflected must hold *exactly* the same "definition" 104edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // as the one defined previously. We say two record types A and B have the 105edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // same definition iff: 106edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // 107edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // struct A { struct B { 108edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Type(a1) a1, Type(b1) b1, 109edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Type(a2) a2, Type(b1) b2, 110edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // ... ... 111edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Type(aN) aN Type(b3) b3, 112edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // }; } 113edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Cond. #1. They have same number of fields, i.e., N = M; 114edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Cond. #2. for (i := 1 to N) 115edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Type(ai) = Type(bi) must hold; 116edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Cond. #3. for (i := 1 to N) 117edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Name(ai) = Name(bi) must hold; 118edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // 119edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // where, 120edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Type(F) = the type of field F and 121edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Name(F) = the field name. 122edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 123edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project bool PassODR = false; 124edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Cond. #1 and Cond. #2 125edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (Reflected->equals(ERT)) { 126edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project // Cond #3. 127edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project RSExportRecordType::const_field_iterator AI = Reflected->fields_begin(), 128edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BI = ERT->fields_begin(); 129edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 130edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project for (unsigned i = 0, e = Reflected->getFields().size(); i != e; i++) { 131edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if ((*AI)->getName() != (*BI)->getName()) 132edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project break; 133edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project AI++; 134edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project BI++; 135edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project } 136edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project PassODR = (AI == (Reflected->fields_end())); 137edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project } 138 139 if (!PassODR) { 140 getDiagnostics().Report(mDiagErrorODR) << Reflected->getName() 141 << getInputFileName() 142 << RD->getValue().second; 143 return false; 144 } 145 } else { 146 llvm::StringMapEntry<ReflectedDefinitionTy> *ME = 147 llvm::StringMapEntry<ReflectedDefinitionTy>::Create(RDKey.begin(), 148 RDKey.end()); 149 ME->setValue(std::make_pair(ERT, CurInputFile)); 150 151 if (!ReflectedDefinitions.insert(ME)) 152 delete ME; 153 154 // Take the ownership of ERT such that it won't be freed in ~RSContext(). 155 ERT->keep(); 156 } 157 } 158 return true; 159} 160 161void SlangRS::initDiagnostic() { 162 clang::Diagnostic &Diag = getDiagnostics(); 163 if (Diag.setDiagnosticGroupMapping("implicit-function-declaration", 164 clang::diag::MAP_ERROR)) 165 Diag.Report(clang::diag::warn_unknown_warning_option) 166 << "implicit-function-declaration"; 167 168 Diag.setDiagnosticMapping( 169 clang::diag::ext_typecheck_convert_discards_qualifiers, 170 clang::diag::MAP_ERROR, 171 clang::SourceLocation()); 172 173 mDiagErrorInvalidOutputDepParameter = 174 Diag.getCustomDiagID(clang::Diagnostic::Error, 175 "invalid parameter for output dependencies files."); 176 177 mDiagErrorODR = 178 Diag.getCustomDiagID(clang::Diagnostic::Error, 179 "type '%0' in different translation unit (%1 v.s. " 180 "%2) has incompatible type definition"); 181 182 mDiagErrorTargetAPIRange = Diag.getCustomDiagID(clang::Diagnostic::Error, 183 "target API level '%0' is out of range ('%1' - '%2')"); 184 185 return; 186} 187 188void SlangRS::initPreprocessor() { 189 clang::Preprocessor &PP = getPreprocessor(); 190 191 std::stringstream RSH; 192 RSH << "#define RS_VERSION " << mTargetAPI << std::endl; 193#define RS_HEADER_ENTRY(name, default_included) \ 194 if (default_included) \ 195 RSH << "#include \"" #name "." RS_HEADER_SUFFIX "\"" << std::endl; 196 ENUM_RS_HEADER() 197#undef RS_HEADER_ENTRY 198 PP.setPredefines(RSH.str()); 199 200 return; 201} 202 203void SlangRS::initASTContext() { 204 mRSContext = new RSContext(getPreprocessor(), 205 getASTContext(), 206 getTargetInfo(), 207 &mPragmas, 208 &mGeneratedFileNames); 209 return; 210} 211 212clang::ASTConsumer 213*SlangRS::createBackend(const clang::CodeGenOptions& CodeGenOpts, 214 llvm::raw_ostream *OS, 215 Slang::OutputType OT) { 216 return new RSBackend(mRSContext, 217 &getDiagnostics(), 218 CodeGenOpts, 219 getTargetOptions(), 220 &mPragmas, 221 OS, 222 OT, 223 getSourceManager(), 224 mAllowRSPrefix, 225 mTargetAPI); 226} 227 228bool SlangRS::IsRSHeaderFile(const char *File) { 229#define RS_HEADER_ENTRY(name, default_included) \ 230 if (::strcmp(File, #name "."RS_HEADER_SUFFIX) == 0) \ 231 return true; 232ENUM_RS_HEADER() 233#undef RS_HEADER_ENTRY 234 return false; 235} 236 237bool SlangRS::IsFunctionInRSHeaderFile(const clang::FunctionDecl *FD, 238 const clang::SourceManager &SourceMgr) { 239 clang::FullSourceLoc FSL(FD->getLocStart(), SourceMgr); 240 clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL); 241 242 return IsRSHeaderFile(llvm::sys::path::filename(PLoc.getFilename()).data()); 243} 244 245SlangRS::SlangRS() : Slang(), mRSContext(NULL), mAllowRSPrefix(false), 246 mTargetAPI(0) { 247 return; 248} 249 250bool SlangRS::compile( 251 const std::list<std::pair<const char*, const char*> > &IOFiles, 252 const std::list<std::pair<const char*, const char*> > &DepFiles, 253 const std::vector<std::string> &IncludePaths, 254 const std::vector<std::string> &AdditionalDepTargets, 255 Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage, 256 bool AllowRSPrefix, bool OutputDep, 257 unsigned int TargetAPI, 258 const std::string &JavaReflectionPathBase, 259 const std::string &JavaReflectionPackageName) { 260 if (IOFiles.empty()) 261 return true; 262 263 if (OutputDep && (DepFiles.size() != IOFiles.size())) { 264 getDiagnostics().Report(mDiagErrorInvalidOutputDepParameter); 265 return false; 266 } 267 268 std::string RealPackageName; 269 270 const char *InputFile, *OutputFile, *BCOutputFile, *DepOutputFile; 271 std::list<std::pair<const char*, const char*> >::const_iterator 272 IOFileIter = IOFiles.begin(), DepFileIter = DepFiles.begin(); 273 274 setIncludePaths(IncludePaths); 275 setOutputType(OutputType); 276 if (OutputDep) { 277 setAdditionalDepTargets(AdditionalDepTargets); 278 } 279 280 mAllowRSPrefix = AllowRSPrefix; 281 282 mTargetAPI = TargetAPI; 283 if (mTargetAPI < RS_MINIMUM_TARGET_API || 284 mTargetAPI > RS_MAXIMUM_TARGET_API) { 285 getDiagnostics().Report(mDiagErrorTargetAPIRange) << mTargetAPI 286 << RS_MINIMUM_TARGET_API 287 << RS_MAXIMUM_TARGET_API; 288 return false; 289 } 290 291 for (unsigned i = 0, e = IOFiles.size(); i != e; i++) { 292 InputFile = IOFileIter->first; 293 OutputFile = IOFileIter->second; 294 295 reset(); 296 297 if (!setInputSource(InputFile)) 298 return false; 299 300 if (!setOutput(OutputFile)) 301 return false; 302 303 if (Slang::compile() > 0) 304 return false; 305 306 if (OutputType != Slang::OT_Dependency) { 307 if (!reflectToJava(JavaReflectionPathBase, 308 JavaReflectionPackageName, 309 &RealPackageName)) 310 return false; 311 312 for (std::vector<std::string>::const_iterator 313 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); 314 I != E; 315 I++) { 316 std::string ReflectedName = RSSlangReflectUtils::ComputePackagedPath( 317 JavaReflectionPathBase.c_str(), 318 (RealPackageName + OS_PATH_SEPARATOR_STR + *I).c_str()); 319 appendGeneratedFileName(ReflectedName + ".java"); 320 } 321 322 if ((OutputType == Slang::OT_Bitcode) && 323 (BitcodeStorage == BCST_JAVA_CODE) && 324 !generateBitcodeAccessor(JavaReflectionPathBase, 325 RealPackageName.c_str())) 326 return false; 327 } 328 329 if (OutputDep) { 330 BCOutputFile = DepFileIter->first; 331 DepOutputFile = DepFileIter->second; 332 333 setDepTargetBC(BCOutputFile); 334 335 if (!setDepOutput(DepOutputFile)) 336 return false; 337 338 if (generateDepFile() > 0) 339 return false; 340 341 DepFileIter++; 342 } 343 344 if (!checkODR(InputFile)) 345 return false; 346 347 IOFileIter++; 348 } 349 350 return true; 351} 352 353void SlangRS::reset() { 354 delete mRSContext; 355 mRSContext = NULL; 356 mGeneratedFileNames.clear(); 357 Slang::reset(); 358 return; 359} 360 361SlangRS::~SlangRS() { 362 delete mRSContext; 363 for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(), 364 E = ReflectedDefinitions.end(); 365 I != E; 366 I++) { 367 delete I->getValue().first; 368 } 369 return; 370} 371 372} // namespace slang 373