slang_rs.cpp revision cc1b9699446aea20773e4c3c6ff5759fedd8ab51
1/* 2 * Copyright 2010-2012, 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 17#include "slang_rs.h" 18 19#include <cstring> 20#include <list> 21#include <sstream> 22#include <string> 23#include <utility> 24#include <vector> 25 26#include "clang/Basic/SourceLocation.h" 27 28#include "clang/Frontend/FrontendDiagnostic.h" 29 30#include "clang/Sema/SemaDiagnostic.h" 31 32#include "llvm/Support/Path.h" 33 34#include "os_sep.h" 35#include "slang_rs_backend.h" 36#include "slang_rs_context.h" 37#include "slang_rs_export_type.h" 38 39#include "slang_rs_reflection.h" 40#include "slang_rs_reflection_cpp.h" 41 42namespace slang { 43 44#define FS_SUFFIX "fs" 45 46#define RS_HEADER_SUFFIX "rsh" 47 48/* RS_HEADER_ENTRY(name) */ 49#define ENUM_RS_HEADER() \ 50 RS_HEADER_ENTRY(rs_allocation) \ 51 RS_HEADER_ENTRY(rs_atomic) \ 52 RS_HEADER_ENTRY(rs_cl) \ 53 RS_HEADER_ENTRY(rs_core) \ 54 RS_HEADER_ENTRY(rs_core_math) \ 55 RS_HEADER_ENTRY(rs_debug) \ 56 RS_HEADER_ENTRY(rs_element) \ 57 RS_HEADER_ENTRY(rs_graphics) \ 58 RS_HEADER_ENTRY(rs_math) \ 59 RS_HEADER_ENTRY(rs_mesh) \ 60 RS_HEADER_ENTRY(rs_matrix) \ 61 RS_HEADER_ENTRY(rs_object) \ 62 RS_HEADER_ENTRY(rs_program) \ 63 RS_HEADER_ENTRY(rs_quaternion) \ 64 RS_HEADER_ENTRY(rs_sampler) \ 65 RS_HEADER_ENTRY(rs_time) \ 66 RS_HEADER_ENTRY(rs_types) \ 67 68// Returns true if \p Filename ends in ".fs". 69bool SlangRS::isFilterscript(const char *Filename) { 70 const char *c = strrchr(Filename, '.'); 71 if (c && !strncmp(FS_SUFFIX, c + 1, strlen(FS_SUFFIX) + 1)) { 72 return true; 73 } else { 74 return false; 75 } 76} 77 78bool SlangRS::generateJavaBitcodeAccessor(const std::string &OutputPathBase, 79 const std::string &PackageName, 80 const std::string *LicenseNote) { 81 RSSlangReflectUtils::BitCodeAccessorContext BCAccessorContext; 82 83 BCAccessorContext.rsFileName = getInputFileName().c_str(); 84 BCAccessorContext.bcFileName = getOutputFileName().c_str(); 85 BCAccessorContext.reflectPath = OutputPathBase.c_str(); 86 BCAccessorContext.packageName = PackageName.c_str(); 87 BCAccessorContext.licenseNote = LicenseNote; 88 BCAccessorContext.bcStorage = BCST_JAVA_CODE; // Must be BCST_JAVA_CODE 89 90 return RSSlangReflectUtils::GenerateJavaBitCodeAccessor(BCAccessorContext); 91} 92 93bool SlangRS::checkODR(const char *CurInputFile) { 94 for (RSContext::ExportableList::iterator I = mRSContext->exportable_begin(), 95 E = mRSContext->exportable_end(); 96 I != E; 97 I++) { 98 RSExportable *RSE = *I; 99 if (RSE->getKind() != RSExportable::EX_TYPE) 100 continue; 101 102 RSExportType *ET = static_cast<RSExportType *>(RSE); 103 if (ET->getClass() != RSExportType::ExportClassRecord) 104 continue; 105 106 RSExportRecordType *ERT = static_cast<RSExportRecordType *>(ET); 107 108 // Artificial record types (create by us not by user in the source) always 109 // conforms the ODR. 110 if (ERT->isArtificial()) 111 continue; 112 113 // Key to lookup ERT in ReflectedDefinitions 114 llvm::StringRef RDKey(ERT->getName()); 115 ReflectedDefinitionListTy::const_iterator RD = 116 ReflectedDefinitions.find(RDKey); 117 118 if (RD != ReflectedDefinitions.end()) { 119 const RSExportRecordType *Reflected = RD->getValue().first; 120 // There's a record (struct) with the same name reflected before. Enforce 121 // ODR checking - the Reflected must hold *exactly* the same "definition" 122 // as the one defined previously. We say two record types A and B have the 123 // same definition iff: 124 // 125 // struct A { struct B { 126 // Type(a1) a1, Type(b1) b1, 127 // Type(a2) a2, Type(b1) b2, 128 // ... ... 129 // Type(aN) aN Type(b3) b3, 130 // }; } 131 // Cond. #1. They have same number of fields, i.e., N = M; 132 // Cond. #2. for (i := 1 to N) 133 // Type(ai) = Type(bi) must hold; 134 // Cond. #3. for (i := 1 to N) 135 // Name(ai) = Name(bi) must hold; 136 // 137 // where, 138 // Type(F) = the type of field F and 139 // Name(F) = the field name. 140 141 bool PassODR = false; 142 // Cond. #1 and Cond. #2 143 if (Reflected->equals(ERT)) { 144 // Cond #3. 145 RSExportRecordType::const_field_iterator AI = Reflected->fields_begin(), 146 BI = ERT->fields_begin(); 147 148 for (unsigned i = 0, e = Reflected->getFields().size(); i != e; i++) { 149 if ((*AI)->getName() != (*BI)->getName()) 150 break; 151 AI++; 152 BI++; 153 } 154 PassODR = (AI == (Reflected->fields_end())); 155 } 156 157 if (!PassODR) { 158 getDiagnostics().Report(mDiagErrorODR) << Reflected->getName() 159 << getInputFileName() 160 << RD->getValue().second; 161 return false; 162 } 163 } else { 164 llvm::StringMapEntry<ReflectedDefinitionTy> *ME = 165 llvm::StringMapEntry<ReflectedDefinitionTy>::Create(RDKey.begin(), 166 RDKey.end()); 167 ME->setValue(std::make_pair(ERT, CurInputFile)); 168 169 if (!ReflectedDefinitions.insert(ME)) 170 delete ME; 171 172 // Take the ownership of ERT such that it won't be freed in ~RSContext(). 173 ERT->keep(); 174 } 175 } 176 return true; 177} 178 179void SlangRS::initDiagnostic() { 180 clang::DiagnosticsEngine &DiagEngine = getDiagnostics(); 181 182 if (DiagEngine.setDiagnosticGroupMapping("implicit-function-declaration", 183 clang::diag::MAP_ERROR)) 184 DiagEngine.Report(clang::diag::warn_unknown_warning_option) 185 << "implicit-function-declaration"; 186 187 DiagEngine.setDiagnosticMapping( 188 clang::diag::ext_typecheck_convert_discards_qualifiers, 189 clang::diag::MAP_ERROR, 190 clang::SourceLocation()); 191 192 mDiagErrorInvalidOutputDepParameter = 193 DiagEngine.getCustomDiagID( 194 clang::DiagnosticsEngine::Error, 195 "invalid parameter for output dependencies files."); 196 197 mDiagErrorODR = 198 DiagEngine.getCustomDiagID( 199 clang::DiagnosticsEngine::Error, 200 "type '%0' in different translation unit (%1 v.s. %2) " 201 "has incompatible type definition"); 202 203 mDiagErrorTargetAPIRange = 204 DiagEngine.getCustomDiagID( 205 clang::DiagnosticsEngine::Error, 206 "target API level '%0' is out of range ('%1' - '%2')"); 207} 208 209void SlangRS::initPreprocessor() { 210 clang::Preprocessor &PP = getPreprocessor(); 211 212 std::stringstream RSH; 213 RSH << "#define RS_VERSION " << mTargetAPI << "\n"; 214 RSH << "#include \"rs_core." RS_HEADER_SUFFIX "\"\n"; 215 PP.setPredefines(RSH.str()); 216} 217 218void SlangRS::initASTContext() { 219 mRSContext = new RSContext(getPreprocessor(), 220 getASTContext(), 221 getTargetInfo(), 222 &mPragmas, 223 mTargetAPI, 224 &mGeneratedFileNames); 225} 226 227clang::ASTConsumer 228*SlangRS::createBackend(const clang::CodeGenOptions& CodeGenOpts, 229 llvm::raw_ostream *OS, 230 Slang::OutputType OT) { 231 return new RSBackend(mRSContext, 232 &getDiagnostics(), 233 CodeGenOpts, 234 getTargetOptions(), 235 &mPragmas, 236 OS, 237 OT, 238 getSourceManager(), 239 mAllowRSPrefix, 240 mIsFilterscript); 241} 242 243bool SlangRS::IsRSHeaderFile(const char *File) { 244#define RS_HEADER_ENTRY(name) \ 245 if (::strcmp(File, #name "." RS_HEADER_SUFFIX) == 0) \ 246 return true; 247ENUM_RS_HEADER() 248#undef RS_HEADER_ENTRY 249 return false; 250} 251 252bool SlangRS::IsLocInRSHeaderFile(const clang::SourceLocation &Loc, 253 const clang::SourceManager &SourceMgr) { 254 clang::FullSourceLoc FSL(Loc, SourceMgr); 255 clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL); 256 257 const char *Filename = PLoc.getFilename(); 258 if (!Filename) { 259 return false; 260 } else { 261 return IsRSHeaderFile(llvm::sys::path::filename(Filename).data()); 262 } 263} 264 265SlangRS::SlangRS() 266 : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0), 267 mIsFilterscript(false) { 268} 269 270bool SlangRS::compile( 271 const std::list<std::pair<const char*, const char*> > &IOFiles, 272 const std::list<std::pair<const char*, const char*> > &DepFiles, 273 const std::vector<std::string> &IncludePaths, 274 const std::vector<std::string> &AdditionalDepTargets, 275 Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage, 276 bool AllowRSPrefix, bool OutputDep, 277 unsigned int TargetAPI, bool EmitDebug, 278 llvm::CodeGenOpt::Level OptimizationLevel, 279 const std::string &JavaReflectionPathBase, 280 const std::string &JavaReflectionPackageName, 281 const std::string &RSPackageName) { 282 if (IOFiles.empty()) 283 return true; 284 285 if (OutputDep && (DepFiles.size() != IOFiles.size())) { 286 getDiagnostics().Report(mDiagErrorInvalidOutputDepParameter); 287 return false; 288 } 289 290 std::string RealPackageName; 291 292 const char *InputFile, *OutputFile, *BCOutputFile, *DepOutputFile; 293 std::list<std::pair<const char*, const char*> >::const_iterator 294 IOFileIter = IOFiles.begin(), DepFileIter = DepFiles.begin(); 295 296 setIncludePaths(IncludePaths); 297 setOutputType(OutputType); 298 if (OutputDep) { 299 setAdditionalDepTargets(AdditionalDepTargets); 300 } 301 302 setDebugMetadataEmission(EmitDebug); 303 304 setOptimizationLevel(OptimizationLevel); 305 306 mAllowRSPrefix = AllowRSPrefix; 307 308 mTargetAPI = TargetAPI; 309 if (mTargetAPI < SLANG_MINIMUM_TARGET_API || 310 mTargetAPI > SLANG_MAXIMUM_TARGET_API) { 311 getDiagnostics().Report(mDiagErrorTargetAPIRange) << mTargetAPI 312 << SLANG_MINIMUM_TARGET_API << SLANG_MAXIMUM_TARGET_API; 313 return false; 314 } 315 316 // Skip generation of warnings a second time if we are doing more than just 317 // a single pass over the input file. 318 bool SuppressAllWarnings = (OutputType != Slang::OT_Dependency); 319 320 for (unsigned i = 0, e = IOFiles.size(); i != e; i++) { 321 InputFile = IOFileIter->first; 322 OutputFile = IOFileIter->second; 323 324 reset(); 325 326 if (!setInputSource(InputFile)) 327 return false; 328 329 if (!setOutput(OutputFile)) 330 return false; 331 332 mIsFilterscript = isFilterscript(InputFile); 333 334 if (Slang::compile() > 0) 335 return false; 336 337 if (!JavaReflectionPackageName.empty()) { 338 mRSContext->setReflectJavaPackageName(JavaReflectionPackageName); 339 } 340 const std::string &RealPackageName = 341 mRSContext->getReflectJavaPackageName(); 342 343 if (OutputType != Slang::OT_Dependency) { 344 345 if (BitcodeStorage == BCST_CPP_CODE) { 346 RSReflectionCpp R(mRSContext); 347 bool ret = R.reflect(JavaReflectionPathBase, getInputFileName(), getOutputFileName()); 348 if (!ret) { 349 return false; 350 } 351 } else { 352 if (!RSPackageName.empty()) { 353 mRSContext->setRSPackageName(RSPackageName); 354 } 355 356 RSReflectionJava R(mRSContext, &mGeneratedFileNames); 357 bool ret = R.reflect(JavaReflectionPathBase, mRSContext->getRSPackageName(), RSPackageName, 358 getInputFileName(), getOutputFileName(), (BitcodeStorage == BCST_JAVA_CODE)); 359 if (!ret) { 360 // TODO Is this needed or will the error message have been printed 361 // already? and why not for the C++ case? 362 fprintf(stderr, 363 "RSContext::reflectToJava : failed to do reflection (%s)\n", 364 R.getLastError()); 365 } 366 367 for (std::vector<std::string>::const_iterator 368 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end(); 369 I != E; 370 I++) { 371 std::string ReflectedName = RSSlangReflectUtils::ComputePackagedPath( 372 JavaReflectionPathBase.c_str(), 373 (RealPackageName + OS_PATH_SEPARATOR_STR + *I).c_str()); 374 appendGeneratedFileName(ReflectedName + ".java"); 375 } 376 377 if ((OutputType == Slang::OT_Bitcode) && 378 (BitcodeStorage == BCST_JAVA_CODE) && 379 !generateJavaBitcodeAccessor(JavaReflectionPathBase, 380 RealPackageName.c_str(), 381 mRSContext->getLicenseNote())) { 382 return false; 383 } 384 } 385 } 386 387 if (OutputDep) { 388 BCOutputFile = DepFileIter->first; 389 DepOutputFile = DepFileIter->second; 390 391 setDepTargetBC(BCOutputFile); 392 393 if (!setDepOutput(DepOutputFile)) 394 return false; 395 396 if (SuppressAllWarnings) { 397 getDiagnostics().setSuppressAllDiagnostics(true); 398 } 399 if (generateDepFile() > 0) 400 return false; 401 if (SuppressAllWarnings) { 402 getDiagnostics().setSuppressAllDiagnostics(false); 403 } 404 405 DepFileIter++; 406 } 407 408 if (!checkODR(InputFile)) 409 return false; 410 411 IOFileIter++; 412 } 413 414 return true; 415} 416 417void SlangRS::reset() { 418 delete mRSContext; 419 mRSContext = NULL; 420 mGeneratedFileNames.clear(); 421 Slang::reset(); 422} 423 424SlangRS::~SlangRS() { 425 delete mRSContext; 426 for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(), 427 E = ReflectedDefinitions.end(); 428 I != E; 429 I++) { 430 delete I->getValue().first; 431 } 432} 433 434} // namespace slang 435