slang_rs.cpp revision 59f22c376b2c1cd109735280689224fadfe40b42
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}
225
226clang::ASTConsumer
227*SlangRS::createBackend(const clang::CodeGenOptions& CodeGenOpts,
228                        llvm::raw_ostream *OS,
229                        Slang::OutputType OT) {
230    return new RSBackend(mRSContext,
231                         &getDiagnostics(),
232                         CodeGenOpts,
233                         getTargetOptions(),
234                         &mPragmas,
235                         OS,
236                         OT,
237                         getSourceManager(),
238                         mAllowRSPrefix,
239                         mIsFilterscript);
240}
241
242bool SlangRS::IsRSHeaderFile(const char *File) {
243#define RS_HEADER_ENTRY(name)  \
244  if (::strcmp(File, #name "." RS_HEADER_SUFFIX) == 0)  \
245    return true;
246ENUM_RS_HEADER()
247#undef RS_HEADER_ENTRY
248  return false;
249}
250
251bool SlangRS::IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
252                                  const clang::SourceManager &SourceMgr) {
253  clang::FullSourceLoc FSL(Loc, SourceMgr);
254  clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL);
255
256  const char *Filename = PLoc.getFilename();
257  if (!Filename) {
258    return false;
259  } else {
260    return IsRSHeaderFile(llvm::sys::path::filename(Filename).data());
261  }
262}
263
264SlangRS::SlangRS()
265  : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0),
266    mIsFilterscript(false) {
267}
268
269bool SlangRS::compile(
270    const std::list<std::pair<const char*, const char*> > &IOFiles,
271    const std::list<std::pair<const char*, const char*> > &DepFiles,
272    const std::vector<std::string> &IncludePaths,
273    const std::vector<std::string> &AdditionalDepTargets,
274    Slang::OutputType OutputType, BitCodeStorageType BitcodeStorage,
275    bool AllowRSPrefix, bool OutputDep,
276    unsigned int TargetAPI, bool EmitDebug,
277    llvm::CodeGenOpt::Level OptimizationLevel,
278    const std::string &JavaReflectionPathBase,
279    const std::string &JavaReflectionPackageName,
280    const std::string &RSPackageName) {
281  if (IOFiles.empty())
282    return true;
283
284  if (OutputDep && (DepFiles.size() != IOFiles.size())) {
285    getDiagnostics().Report(mDiagErrorInvalidOutputDepParameter);
286    return false;
287  }
288
289  std::string RealPackageName;
290
291  const char *InputFile, *OutputFile, *BCOutputFile, *DepOutputFile;
292  std::list<std::pair<const char*, const char*> >::const_iterator
293      IOFileIter = IOFiles.begin(), DepFileIter = DepFiles.begin();
294
295  setIncludePaths(IncludePaths);
296  setOutputType(OutputType);
297  if (OutputDep) {
298    setAdditionalDepTargets(AdditionalDepTargets);
299  }
300
301  setDebugMetadataEmission(EmitDebug);
302
303  setOptimizationLevel(OptimizationLevel);
304
305  mAllowRSPrefix = AllowRSPrefix;
306
307  mTargetAPI = TargetAPI;
308  if (mTargetAPI < SLANG_MINIMUM_TARGET_API ||
309      mTargetAPI > SLANG_MAXIMUM_TARGET_API) {
310    getDiagnostics().Report(mDiagErrorTargetAPIRange) << mTargetAPI
311        << SLANG_MINIMUM_TARGET_API << SLANG_MAXIMUM_TARGET_API;
312    return false;
313  }
314
315  // Skip generation of warnings a second time if we are doing more than just
316  // a single pass over the input file.
317  bool SuppressAllWarnings = (OutputType != Slang::OT_Dependency);
318
319  for (unsigned i = 0, e = IOFiles.size(); i != e; i++) {
320    InputFile = IOFileIter->first;
321    OutputFile = IOFileIter->second;
322
323    reset();
324
325    if (!setInputSource(InputFile))
326      return false;
327
328    if (!setOutput(OutputFile))
329      return false;
330
331    mIsFilterscript = isFilterscript(InputFile);
332
333    if (Slang::compile() > 0)
334      return false;
335
336    if (!JavaReflectionPackageName.empty()) {
337      mRSContext->setReflectJavaPackageName(JavaReflectionPackageName);
338    }
339    const std::string &RealPackageName =
340        mRSContext->getReflectJavaPackageName();
341
342    if (OutputType != Slang::OT_Dependency) {
343
344      if (BitcodeStorage == BCST_CPP_CODE) {
345          RSReflectionCpp R(mRSContext, JavaReflectionPathBase, getInputFileName(),
346                            getOutputFileName());
347          if (!R.reflect()) {
348            return false;
349          }
350      } else {
351        if (!RSPackageName.empty()) {
352          mRSContext->setRSPackageName(RSPackageName);
353        }
354
355        RSReflectionJava R(mRSContext, &mGeneratedFileNames,
356                           JavaReflectionPathBase, getInputFileName(),
357                           getOutputFileName(),
358                           BitcodeStorage == BCST_JAVA_CODE);
359        if (!R.reflect()) {
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, "RSContext::reflectToJava : failed to do reflection "
363                          "(%s)\n",
364                  R.getLastError());
365          return false;
366        }
367
368        for (std::vector<std::string>::const_iterator
369                 I = mGeneratedFileNames.begin(), E = mGeneratedFileNames.end();
370             I != E;
371             I++) {
372          std::string ReflectedName = RSSlangReflectUtils::ComputePackagedPath(
373              JavaReflectionPathBase.c_str(),
374              (RealPackageName + OS_PATH_SEPARATOR_STR + *I).c_str());
375          appendGeneratedFileName(ReflectedName + ".java");
376        }
377
378        if ((OutputType == Slang::OT_Bitcode) &&
379            (BitcodeStorage == BCST_JAVA_CODE) &&
380            !generateJavaBitcodeAccessor(JavaReflectionPathBase,
381                                         RealPackageName.c_str(),
382                                         mRSContext->getLicenseNote())) {
383          return false;
384        }
385      }
386    }
387
388    if (OutputDep) {
389      BCOutputFile = DepFileIter->first;
390      DepOutputFile = DepFileIter->second;
391
392      setDepTargetBC(BCOutputFile);
393
394      if (!setDepOutput(DepOutputFile))
395        return false;
396
397      if (SuppressAllWarnings) {
398        getDiagnostics().setSuppressAllDiagnostics(true);
399      }
400      if (generateDepFile() > 0)
401        return false;
402      if (SuppressAllWarnings) {
403        getDiagnostics().setSuppressAllDiagnostics(false);
404      }
405
406      DepFileIter++;
407    }
408
409    if (!checkODR(InputFile))
410      return false;
411
412    IOFileIter++;
413  }
414
415  return true;
416}
417
418void SlangRS::reset() {
419  delete mRSContext;
420  mRSContext = NULL;
421  mGeneratedFileNames.clear();
422  Slang::reset();
423}
424
425SlangRS::~SlangRS() {
426  delete mRSContext;
427  for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(),
428          E = ReflectedDefinitions.end();
429       I != E;
430       I++) {
431    delete I->getValue().first;
432  }
433}
434
435}  // namespace slang
436