1 //
2 // attributes.cpp: Rcpp R/C++ interface class library -- Rcpp attributes
3 //
4 // Copyright (C) 2012 - 2020  JJ Allaire, Dirk Eddelbuettel and Romain Francois
5 // Copyright (C) 2021         JJ Allaire, Dirk Eddelbuettel, Romain Francois and Iñaki Ucar
6 //
7 // This file is part of Rcpp.
8 //
9 // Rcpp is free software: you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Rcpp is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Rcpp.  If not, see <http://www.gnu.org/licenses/>.
21 
22 #define COMPILING_RCPP
23 
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 
28 #include <cstring>
29 
30 #include <string>
31 #include <vector>
32 #include <map>
33 #include <set>
34 #include <algorithm>
35 #include <fstream>
36 #include <sstream>
37 
38 #define RCPP_NO_SUGAR
39 #include <Rcpp.h>
40 
41 /*******************************************************************
42  * AttributesUtil.h
43  *******************************************************************/
44 
45 namespace Rcpp {
46 namespace attributes {
47 
48     // Utility class for getting file existence and last modified time
49     class FileInfo {
50     public:
51 
52         // create from path
53         explicit FileInfo(const std::string& path);
54 
55         // create from R list
FileInfo(const List & fileInfo)56         explicit FileInfo(const List& fileInfo) {		// #nocov start
57             path_ = as<std::string>(fileInfo["path"]);
58             exists_ = as<bool>(fileInfo["exists"]);
59             lastModified_ = as<double>(fileInfo["lastModified"]);
60         }							// #nocov end
61 
62         // convert to R list
toList() const63         List toList() const {
64             List fileInfo;
65             fileInfo["path"] = path_;
66             fileInfo["exists"] = exists_;
67             fileInfo["lastModified"] = lastModified_;
68             return fileInfo;
69         }
70 
path() const71         std::string path() const { return path_; }
exists() const72         bool exists() const { return exists_; }
lastModified() const73         double lastModified() const { return lastModified_; }
74 
extension() const75         std::string extension() const {
76             std::string::size_type pos = path_.find_last_of('.');
77             if (pos != std::string::npos)
78                 return path_.substr(pos);
79             else
80                 return "";					// #nocov
81         }
82 
operator <(const FileInfo & other) const83         bool operator<(const FileInfo& other) const {
84             return path_ < other.path_;
85         };
86 
operator ==(const FileInfo & other) const87         bool operator==(const FileInfo& other) const {
88             return path_ == other.path_ &&
89                    exists_ == other.exists_ &&
90                    lastModified_ == other.lastModified_;
91         };
92 
operator !=(const FileInfo & other) const93         bool operator!=(const FileInfo& other) const {
94             return ! (*this == other);
95         };
96 
operator <<(std::ostream & os) const97         std::ostream& operator<<(std::ostream& os) const {
98             os << path_;
99             return os;
100         }
101 
102     private:
103         std::string path_;
104         bool exists_;
105         double lastModified_;
106     };
107 
108     // Remove a file
109     bool removeFile(const std::string& path);
110 
111     // Recursively create a directory
112     void createDirectory(const std::string& path);
113 
114     // Known whitespace chars
115     extern const char * const kWhitespaceChars;
116 
117     // Query whether a character is whitespace
118     bool isWhitespace(char ch);
119 
120     // Trim a string
121     void trimWhitespace(std::string* pStr);
122 
123     // Strip trailing line comments
124     void stripTrailingLineComments(std::string* pStr);
125 
126     // Strip balanced quotes from around a string (assumes already trimmed)
127     void stripQuotes(std::string* pStr);
128 
129     // is the passed string quoted?
130     bool isQuoted(const std::string& str);
131 
132     // does a string end with another string?
133     bool endsWith(const std::string& str, const std::string& suffix);
134 
135     // show a warning message
136     void showWarning(const std::string& msg);
137 
138     // is the line a C++ roxygen comment? (started with //')
139     bool isRoxygenCpp(const std::string& str);
140 
141 } // namespace attributes
142 } // namespace Rcpp
143 
144 
145 /*******************************************************************
146  * AttributesTypes.h
147  *******************************************************************/
148 
149 namespace Rcpp {
150 namespace attributes {
151 
152     // Known attribute names & parameters
153     const char * const kExportAttribute = "export";
154     const char * const kExportName = "name";
155     const char * const kExportRng = "rng";
156     const char * const kExportInvisible = "invisible";
157     const char * const kInitAttribute = "init";
158     const char * const kDependsAttribute = "depends";
159     const char * const kPluginsAttribute = "plugins";
160     const char * const kInterfacesAttribute = "interfaces";
161     const char * const kInterfaceR = "r";
162     const char * const kInterfaceCpp = "cpp";
163     const char * const kParamValueFalse = "false";
164     const char * const kParamValueTrue = "true";
165     const char * const kParamValueFALSE = "FALSE";
166     const char * const kParamValueTRUE = "TRUE";
167 
168     // Type info
169     class Type {
170     public:
Type()171         Type(): isConst_(false), isReference_(false) {}
Type(const std::string & name,bool isConst,bool isReference)172         Type(const std::string& name, bool isConst, bool isReference)
173             : name_(name), isConst_(isConst), isReference_(isReference)
174         {
175         }
empty() const176         bool empty() const { return name().empty(); }
177 
operator ==(const Type & other) const178         bool operator==(const Type& other) const {		// #nocov start
179             return name_ == other.name_ &&
180                    isConst_ == other.isConst_ &&
181                    isReference_ == other.isReference_;
182         };							// #nocov end
183 
operator !=(const Type & other) const184         bool operator!=(const Type& other) const {
185             return !(*this == other);
186         };
187 
name() const188         const std::string& name() const { return name_; }
full_name() const189         std::string full_name() const {
190             std::string res ;
191             if( isConst() ) res += "const " ;
192             res += name() ;
193             if( isReference() ) res += "&" ;
194             return res ;
195         }
196 
isVoid() const197         bool isVoid() const { return name() == "void"; }
isConst() const198         bool isConst() const { return isConst_; }
isReference() const199         bool isReference() const { return isReference_; }
200 
201     private:
202         std::string name_;
203         bool isConst_;
204         bool isReference_;
205     };
206 
207     // Argument info
208     class Argument {
209     public:
Argument()210         Argument() {}
Argument(const std::string & name,const Type & type,const std::string & defaultValue)211         Argument(const std::string& name,
212                  const Type& type,
213                  const std::string& defaultValue)
214             : name_(name), type_(type), defaultValue_(defaultValue)
215         {
216         }
217 
empty() const218         bool empty() const { return type().empty(); }
219 
operator ==(const Argument & other) const220         bool operator==(const Argument& other) const {		// #nocov start
221             return name_ == other.name_ &&
222                    type_ == other.type_ &&
223                    defaultValue_ == other.defaultValue_;
224         };							// #nocov end
225 
operator !=(const Argument & other) const226         bool operator!=(const Argument& other) const {
227             return !(*this == other);
228         };
229 
230 
name() const231         const std::string& name() const { return name_; }
type() const232         const Type& type() const { return type_; }
defaultValue() const233         const std::string& defaultValue() const { return defaultValue_; }
234 
235     private:
236         std::string name_;
237         Type type_;
238         std::string defaultValue_;
239     };
240 
241     // Function info
242     class Function {
243     public:
Function()244         Function() {}
Function(const Type & type,const std::string & name,const std::vector<Argument> & arguments)245         Function(const Type& type,
246                  const std::string& name,
247                  const std::vector<Argument>& arguments)
248             : type_(type), name_(name), arguments_(arguments)
249         {
250         }
251 
renamedTo(const std::string & name) const252         Function renamedTo(const std::string& name) const {	// #nocov start
253             return Function(type(), name, arguments());
254         }
255 
signature() const256         std::string signature() const { return signature(name()); }
257         std::string signature(const std::string& name) const;
258 
isHidden() const259         bool isHidden() const {
260             return name().find_first_of('.') == 0;
261         }							// #nocov end
262 
empty() const263         bool empty() const { return name().empty(); }
264 
operator ==(const Function & other) const265         bool operator==(const Function& other) const {		// #nocov start
266             return type_ == other.type_ &&
267                    name_ == other.name_ &&
268                    arguments_ == other.arguments_;
269         };							// #nocov end
270 
operator !=(const Function & other) const271         bool operator!=(const Function& other) const {
272             return !(*this == other);
273         };
274 
type() const275         const Type& type() const { return type_; }
name() const276         const std::string& name() const { return name_; }
arguments() const277         const std::vector<Argument>& arguments() const { return arguments_; }
278 
279     private:
280         Type type_;
281         std::string name_;
282         std::vector<Argument> arguments_;
283     };
284 
285     // Attribute parameter (with optional value)
286     class Param {
287     public:
Param()288         Param() {}
289         explicit Param(const std::string& paramText);
empty() const290         bool empty() const { return name().empty(); }
291 
operator ==(const Param & other) const292         bool operator==(const Param& other) const {		// #nocov start
293             return name_ == other.name_ &&
294                    value_ == other.value_;
295         };							// #nocov end
296 
operator !=(const Param & other) const297         bool operator!=(const Param& other) const {
298             return !(*this == other);
299         };
300 
301 
name() const302         const std::string& name() const { return name_; }
value() const303         const std::string& value() const { return value_; }	// #nocov
304 
305     private:
306         std::string name_;
307         std::string value_;
308     };
309 
310     // Attribute (w/ optional params and signature of function it qualifies)
311     class Attribute {
312     public:
Attribute()313         Attribute() {}
Attribute(const std::string & name,const std::vector<Param> & params,const Function & function,const std::vector<std::string> & roxygen)314         Attribute(const std::string& name,
315                   const std::vector<Param>& params,
316                   const Function& function,
317                   const std::vector<std::string>& roxygen)
318             : name_(name), params_(params), function_(function), roxygen_(roxygen)
319         {
320         }
321 
empty() const322         bool empty() const { return name().empty(); }		// #nocov start
323 
operator ==(const Attribute & other) const324         bool operator==(const Attribute& other) const {
325             return name_ == other.name_ &&
326                    params_ == other.params_ &&
327                    function_ == other.function_ &&
328                    roxygen_ == other.roxygen_;
329         };							// #nocov end
330 
operator !=(const Attribute & other) const331         bool operator!=(const Attribute& other) const {
332             return !(*this == other);
333         };
334 
335 
name() const336         const std::string& name() const { return name_; }
337 
params() const338         const std::vector<Param>& params() const { return params_; }
339 
340         Param paramNamed(const std::string& name) const;
341 
hasParameter(const std::string & name) const342         bool hasParameter(const std::string& name) const {
343             return !paramNamed(name).empty();
344         }
345 
function() const346         const Function& function() const { return function_; }
347 
isExportedFunction() const348         bool isExportedFunction() const {
349             return (name() == kExportAttribute) && !function().empty();
350         }
351 
exportedName() const352         std::string exportedName() const {
353 
354             // check for explicit name parameter
355             if (hasParameter(kExportName))
356             {
357                 return paramNamed(kExportName).value();		// #nocov
358             }
359             // otherwise un-named parameter in the first slot
360             else if (!params().empty() && params()[0].value().empty())
361             {
362                 return params()[0].name();			// #nocov
363             }
364             // otherwise the actual function name
365             {
366                 return function().name();
367             }
368         }
369 
exportedCppName() const370         std::string exportedCppName() const {			// #nocov start
371             std::string name = exportedName();
372             std::replace(name.begin(), name.end(), '.', '_');
373             return name;
374         }							// #nocov end
375 
rng() const376         bool rng() const {
377             Param rngParam = paramNamed(kExportRng);
378             if (!rngParam.empty())
379                 return rngParam.value() == kParamValueTrue ||	// #nocov
380                        rngParam.value() == kParamValueTRUE;  	// #nocov
381             else
382                 return true;
383         }
384 
invisible() const385         bool invisible() const {
386             Param invisibleParam = paramNamed(kExportInvisible);
387             if (!invisibleParam.empty())
388                 return invisibleParam.value() == kParamValueTrue ||	// #nocov
389                        invisibleParam.value() == kParamValueTRUE;  	// #nocov
390             else
391                 return false;
392         }
393 
roxygen() const394         const std::vector<std::string>& roxygen() const { return roxygen_; }
395 
396     private:
397         std::string name_;
398         std::vector<Param> params_;
399         Function function_;
400         std::vector<std::string> roxygen_;
401     };
402 
403     // Operator << for parsed types
404     std::ostream& operator<<(std::ostream& os, const Type& type);
405     std::ostream& operator<<(std::ostream& os, const Argument& argument);
406     std::ostream& operator<<(std::ostream& os, const Function& function);
407     std::ostream& operator<<(std::ostream& os, const Param& param);
408     std::ostream& operator<<(std::ostream& os, const Attribute& attribute);
409 
410     // interface to source file attributes
411     class SourceFileAttributes
412     {
413     public:
~SourceFileAttributes()414         virtual ~SourceFileAttributes() {};
415         virtual const std::string& sourceFile() const = 0;
416         virtual bool hasInterface(const std::string& name) const = 0;
417 
418         typedef std::vector<Attribute>::const_iterator const_iterator;
419         virtual const_iterator begin() const = 0;
420         virtual const_iterator end() const = 0;
421 
422         virtual const std::vector<std::string>& modules() const = 0;
423 
424         virtual const std::vector<std::vector<std::string> >& roxygenChunks() const = 0;
425 
426         virtual bool hasGeneratorOutput() const = 0;
427 
428         virtual bool hasPackageInit() const = 0;
429     };
430 
431 
432 } // namespace attributes
433 } // namespace Rcpp
434 
435 
436 
437 /*******************************************************************
438  * AttributesParser.h
439  *******************************************************************/
440 
441 namespace Rcpp {
442 namespace attributes {
443 
444     // Helper class for determining whether we are in a comment
445     class CommentState {
446     public:
CommentState()447         CommentState() : inComment_(false) {}
448     private:
449         // prohibit copying
450         CommentState(const CommentState&);
451         CommentState& operator=(const CommentState&);
452     public:
inComment() const453         bool inComment() const { return inComment_; }
454         void submitLine(const std::string& line);
reset()455         void reset() { inComment_ = false; }
456     private:
457         bool inComment_;
458     };
459 
460     // Class used to parse and return attribute information from a source file
461     class SourceFileAttributesParser : public SourceFileAttributes {
462     public:
463         explicit SourceFileAttributesParser(const std::string& sourceFile,
464                                             const std::string& packageFile,
465                                             bool parseDependencies);
466 
467     private:
468         // prohibit copying
469         SourceFileAttributesParser(const SourceFileAttributesParser&);
470         SourceFileAttributesParser& operator=(const SourceFileAttributesParser&);
471 
472     public:
473         // implemetnation of SourceFileAttributes interface
sourceFile() const474         virtual const std::string& sourceFile() const {		// #nocov
475             return sourceFile_;					// #nocov
476         }
begin() const477         virtual const_iterator begin() const { return attributes_.begin(); }
end() const478         virtual const_iterator end() const { return attributes_.end(); }
479 
modules() const480         virtual const std::vector<std::string>& modules() const
481         {
482             return modules_;
483         }
484 
roxygenChunks() const485         virtual const std::vector<std::vector<std::string> >& roxygenChunks() const {
486             return roxygenChunks_;
487         }
488 
hasGeneratorOutput() const489         virtual bool hasGeneratorOutput() const
490         {
491             return !attributes_.empty() ||
492                    !modules_.empty() ||
493                    !roxygenChunks_.empty();
494         }
495 
hasInterface(const std::string & name) const496         virtual bool hasInterface(const std::string& name) const {
497 
498             for (const_iterator it=begin(); it != end(); ++it) {
499                 if (it->name() == kInterfacesAttribute) {
500                     return it->hasParameter(name);		// #nocov
501                 }
502             }
503 
504             // if there's no interfaces attrbute we default to R
505             if (name == kInterfaceR)
506                 return true;
507             else
508                 return false;
509         }
510 
511         // Was a package init function found?
hasPackageInit() const512         bool hasPackageInit() const {
513             return hasPackageInit_;
514         }
515 
516         // Get lines of embedded R code
embeddedR() const517         const std::vector<std::string>& embeddedR() const {
518             return embeddedR_;
519         }
520 
521         // Get source dependencies
sourceDependencies() const522         const std::vector<FileInfo>& sourceDependencies() const {
523             return sourceDependencies_;
524         };
525 
526     private:
527 
528         // Parsing helpers
529         Attribute parseAttribute(const std::vector<std::string>& match,
530                                  int lineNumber);
531         std::vector<Param> parseParameters(const std::string& input);
532         Function parseFunction(size_t lineNumber);
533         std::string parseSignature(size_t lineNumber);
534         std::vector<std::string> parseArguments(const std::string& argText);
535         Type parseType(const std::string& text);
536 
537         // Validation helpers
538         bool isKnownAttribute(const std::string& name) const;
539         void attributeWarning(const std::string& message,
540                               const std::string& attribute,
541                               size_t lineNumber);
542         void attributeWarning(const std::string& message, size_t lineNumber);
543         void rcppExportWarning(const std::string& message, size_t lineNumber);
544         void rcppExportNoFunctionFoundWarning(size_t lineNumber);
545         void rcppExportInvalidParameterWarning(const std::string& param,
546                                                size_t lineNumber);
547         void rcppInterfacesWarning(const std::string& message,
548                                    size_t lineNumber);
549 
550     private:
551         std::string sourceFile_;
552         CharacterVector lines_;
553         std::vector<Attribute> attributes_;
554         std::vector<std::string> modules_;
555         bool hasPackageInit_;
556         std::vector<std::string> embeddedR_;
557         std::vector<FileInfo> sourceDependencies_;
558         std::vector<std::vector<std::string> > roxygenChunks_;
559         std::vector<std::string> roxygenBuffer_;
560     };
561 
562 } // namespace attributes
563 } // namespace Rcpp
564 
565 
566 /*******************************************************************
567  * AttributesGen.h
568  *******************************************************************/
569 
570 namespace Rcpp {
571 namespace attributes {
572 
573     // Abstract class which manages writing of code for compileAttributes
574     class ExportsGenerator {
575     protected:
576         ExportsGenerator(const std::string& targetFile,
577                          const std::string& package,
578                          const std::string& commentPrefix);
579 
580     private:
581         // prohibit copying
582         ExportsGenerator(const ExportsGenerator&);
583         ExportsGenerator& operator=(const ExportsGenerator&);
584 
585     public:
~ExportsGenerator()586         virtual ~ExportsGenerator() {}
587 
588         // Name of target file and package
targetFile() const589         const std::string& targetFile() const { return targetFile_; }
package() const590         const std::string& package() const { return package_; }
packageCpp() const591         const std::string& packageCpp() const { return packageCpp_; }
packageCppPrefix() const592         const std::string packageCppPrefix() const { return "_" + packageCpp(); }
593 
594         // Abstract interface for code generation
595         virtual void writeBegin() = 0;
596         void writeFunctions(const SourceFileAttributes& attributes,
597                             bool verbose); // see doWriteFunctions below
598         virtual void writeEnd(bool hasPackageInit) = 0;
599 
600         virtual bool commit(const std::vector<std::string>& includes) = 0;
601 
602         // Remove the generated file entirely
603         bool remove();
604 
605         // Allow generator to appear as a std::ostream&
operator std::ostream&()606         operator std::ostream&() {
607             return codeStream_;
608         }
609 
610     protected:
611 
612         // Allow access to the output stream
ostr()613         std::ostream& ostr() {
614             return codeStream_;
615         }
616 
hasCppInterface() const617         bool hasCppInterface() const {
618             return hasCppInterface_;
619         }
620 
621         // Shared knowledge about function namees
exportValidationFunction()622         std::string exportValidationFunction() {
623             return "RcppExport_validate";
624         }
exportValidationFunctionRegisteredName()625         std::string exportValidationFunctionRegisteredName() {
626             return packageCppPrefix() + "_" + exportValidationFunction();
627         }
registerCCallableExportedName()628         std::string registerCCallableExportedName() {			// #nocov
629             return packageCppPrefix() + "_RcppExport_registerCCallable";	// #nocov
630         }
631 
632         // Commit the stream -- is a no-op if the existing code is identical
633         // to the generated code. Returns true if data was written and false
634         // if it wasn't (throws exception on io error)
635         bool commit(const std::string& preamble = std::string());
636 
637         // Convert a dot in package name to underscore for use in header file name
638         std::string dotNameHelper(const std::string & name) const;
639 
640     private:
641 
642         // Private virtual for doWriteFunctions so the base class
643         // can always intercept writeFunctions
644         virtual void doWriteFunctions(const SourceFileAttributes& attributes,
645                                       bool verbose) = 0;
646 
647         // Check whether it's safe to overwrite this file (i.e. whether we
648         // generated the file in the first place)
isSafeToOverwrite() const649         bool isSafeToOverwrite() const {
650             return existingCode_.empty() ||
651                    (existingCode_.find(generatorToken()) != std::string::npos);
652         }
653 
654         // UUID that we write into a comment within the file (so that we can
655         // strongly identify that a file was generated by us before overwriting it)
generatorToken() const656         std::string generatorToken() const {
657             return "10BE3573-1514-4C36-9D1C-5A225CD40393";
658         }
659 
660     private:
661         std::string targetFile_;
662         std::string package_;
663         std::string packageCpp_;
664         std::string commentPrefix_;
665         std::string existingCode_;
666         std::ostringstream codeStream_;
667         bool hasCppInterface_;
668     };
669 
670     // Class which manages generating RcppExports.cpp
671     class CppExportsGenerator : public ExportsGenerator {
672     public:
673         explicit CppExportsGenerator(const std::string& packageDir,
674                                      const std::string& package,
675                                      const std::string& fileSep);
676 
writeBegin()677         virtual void writeBegin() {};
678         virtual void writeEnd(bool hasPackageInit);
679         virtual bool commit(const std::vector<std::string>& includes);
680 
681     private:
682         virtual void doWriteFunctions(const SourceFileAttributes& attributes,
683                                       bool verbose);
684 
685         std::string registerCCallable(size_t indent,
686                                       const std::string& exportedName,
687                                       const std::string& name) const;
688 
689     private:
690         // for generating calls to init functions
691         std::vector<Attribute> initFunctions_;
692 
693         // for generating C++ interfaces
694         std::vector<Attribute> cppExports_;
695 
696         // for generating Rcpp::export native routine registration
697         std::vector<Attribute> nativeRoutines_;
698 
699         // for generating module native routine registration
700         std::vector<std::string> modules_;
701     };
702 
703     // Class which manages generating PackageName_RcppExports.h header file
704     class CppExportsIncludeGenerator : public ExportsGenerator {
705     public:
706         CppExportsIncludeGenerator(const std::string& packageDir,
707                                    const std::string& package,
708                                    const std::string& fileSep);
709 
710         virtual void writeBegin();
711         virtual void writeEnd(bool hasPackageInit);
712         virtual bool commit(const std::vector<std::string>& includes);
713 
714     private:
715         virtual void doWriteFunctions(const SourceFileAttributes& attributes,
716                                       bool verbose);
717         std::string getCCallable(const std::string& function) const;
718         std::string getHeaderGuard() const;
719 
720     private:
721         std::string includeDir_;
722     };
723 
724     // Class which manages generating PackageName.h header file
725     class CppPackageIncludeGenerator : public ExportsGenerator {
726     public:
727         CppPackageIncludeGenerator(const std::string& packageDir,
728                                    const std::string& package,
729                                    const std::string& fileSep);
730 
writeBegin()731         virtual void writeBegin() {}
732         virtual void writeEnd(bool hasPackageInit);
733         virtual bool commit(const std::vector<std::string>& includes);
734 
735     private:
doWriteFunctions(const SourceFileAttributes &,bool)736         virtual void doWriteFunctions(const SourceFileAttributes&, bool) {}
737         std::string getHeaderGuard() const;
738 
739     private:
740         std::string includeDir_;
741     };
742 
743 
744     // Class which manages generator RcppExports.R
745     class RExportsGenerator : public ExportsGenerator {
746     public:
747         RExportsGenerator(const std::string& packageDir,
748                           const std::string& package,
749                           bool registration,
750                           const std::string& fileSep);
751 
writeBegin()752         virtual void writeBegin() {}
753         virtual void writeEnd(bool hasPackageInit);
754         virtual bool commit(const std::vector<std::string>& includes);
755 
756     private:
757         virtual void doWriteFunctions(const SourceFileAttributes& attributes,
758                                       bool verbose);
759 
760         bool registration_;
761 
762     };
763 
764     // Class to manage and dispatch to a list of generators
765     class ExportsGenerators {
766     public:
767         typedef std::vector<ExportsGenerator*>::iterator Itr;
768 
ExportsGenerators()769         ExportsGenerators() {}
770         virtual ~ExportsGenerators();
771 
772         void add(ExportsGenerator* pGenerator);
773 
774         void writeBegin();
775         void writeFunctions(const SourceFileAttributes& attributes,
776                             bool verbose);
777         void writeEnd(bool hasPackageInit);
778 
779         // Commit and return a list of the files that were updated
780         std::vector<std::string> commit(
781                                 const std::vector<std::string>& includes);
782 
783         // Remove and return a list of files that were removed
784         std::vector<std::string> remove();
785 
786     private:
787         // prohibit copying
788         ExportsGenerators(const ExportsGenerators&);
789         ExportsGenerators& operator=(const ExportsGenerators&);
790 
791     private:
792         std::vector<ExportsGenerator*> generators_;
793     };
794 
795     // Standalone generation helpers (used by sourceCpp)
796 
797     std::string generateRArgList(const Function& function);
798 
799     void initializeGlobals(std::ostream& ostr);
800 
801     void generateCpp(std::ostream& ostr,
802                      const SourceFileAttributes& attributes,
803                      bool includePrototype,
804                      bool cppInterface,
805                      const std::string& contextId);
806 
807 } // namespace attributes
808 } // namespace Rcpp
809 
810 
811 /*******************************************************************
812  * AttributesParser.cpp
813  *******************************************************************/
814 
815 namespace Rcpp {
816 namespace attributes {
817 
818     namespace {
819 
regexMatches(Rcpp::CharacterVector lines,const std::string & regex)820         Rcpp::List regexMatches(Rcpp::CharacterVector lines,
821                                 const std::string& regex)
822         {
823             Rcpp::Environment base("package:base");
824             Rcpp::Function regexec = base["regexec"];
825             Rcpp::Function regmatches = base["regmatches"];
826             Rcpp::RObject result =  regexec(regex, lines);
827             Rcpp::List matches = regmatches(lines, result);
828             return matches;
829         }
830 
831         template <typename Stream>
readFile(const std::string & file,Stream & os)832         void readFile(const std::string& file, Stream& os) {
833             std::ifstream ifs(file.c_str());
834             if (ifs.fail())
835                 throw Rcpp::file_io_error(file);		// #nocov
836             os << ifs.rdbuf();
837             ifs.close();
838         }
839 
840         template <typename Collection>
readLines(std::istream & is,Collection * pLines)841         void readLines(std::istream& is, Collection* pLines) {
842             pLines->clear();
843             std::string line;
844             while(std::getline(is, line)) {
845                 // strip \r (for the case of windows line terminators on posix)
846                 if (line.length() > 0 && *line.rbegin() == '\r')
847                     line.erase(line.length()-1, 1);
848                 stripTrailingLineComments(&line);
849                 pLines->push_back(line);
850             }
851         }
852 
addUniqueDependency(Rcpp::CharacterVector include,std::vector<FileInfo> * pDependencies)853         bool addUniqueDependency(Rcpp::CharacterVector include,
854                                  std::vector<FileInfo>* pDependencies) {
855 
856             // return false if we already have this include
857             std::string path = Rcpp::as<std::string>(include);
858             for (size_t i = 0; i<pDependencies->size(); ++i) {
859                 if (pDependencies->at(i).path() == path)
860                     return false;
861             }
862 
863             // add it and return true
864             pDependencies->push_back(FileInfo(path));
865             return true;
866         }
867 
parseSourceDependencies(const std::string & sourceFile,std::vector<FileInfo> * pDependencies)868         void parseSourceDependencies(const std::string& sourceFile,
869                                      std::vector<FileInfo>* pDependencies) {
870 
871             // import R functions
872             Rcpp::Environment baseEnv = Rcpp::Environment::base_env();
873             Rcpp::Function dirname = baseEnv["dirname"];
874             Rcpp::Function filepath = baseEnv["file.path"];
875             Rcpp::Function normalizePath = baseEnv["normalizePath"];
876             Rcpp::Function fileExists = baseEnv["file.exists"];
877             Rcpp::Environment toolsEnv = Rcpp::Environment::namespace_env(
878                                                                     "tools");
879             Rcpp::Function filePathSansExt = toolsEnv["file_path_sans_ext"];
880 
881             // get the path to the source file's directory
882             Rcpp::CharacterVector sourceDir = dirname(sourceFile);
883 
884             // read the source file into a buffer
885             std::stringstream buffer;
886             readFile(sourceFile, buffer);
887 
888             // Now read into a list of strings (which we can pass to regexec)
889             // First read into a std::deque (which will handle lots of append
890             // operations efficiently) then copy into an R chracter vector
891             std::deque<std::string> lines;
892             readLines(buffer, &lines);
893             Rcpp::CharacterVector linesVector = Rcpp::wrap(lines);
894 
895             // look for local includes
896             Rcpp::List matches = regexMatches(
897                             linesVector, "^\\s*#include\\s*\"([^\"]+)\"\\s*$");
898 
899             // accumulate local includes (skip commented sections)
900             CommentState commentState;
901             std::vector<FileInfo> newDependencies;
902             for (int i = 0; i<matches.size(); i++) {
903                 std::string line = lines[i];
904                 commentState.submitLine(line);
905                 if (!commentState.inComment()) {
906                     // get the match
907                     const Rcpp::CharacterVector match = matches[i];
908                     if (match.size() == 2) {
909                         // compose a full file path for the match
910                         Rcpp::CharacterVector include =
911                             filepath(sourceDir, std::string(match[1]));
912                         // if it exists then normalize and add to our list
913                         LogicalVector exists = fileExists(include);
914                         if (exists[0]) {
915                             include = normalizePath(include, "/");
916                             if (addUniqueDependency(include, pDependencies)) {
917                                 newDependencies.push_back(
918                                     FileInfo(Rcpp::as<std::string>(include)));
919                             }
920 
921                             std::vector<std::string> exts;
922                             exts.push_back(".cc");
923                             exts.push_back(".cpp");
924                             for (size_t i = 0; i<exts.size(); ++i) {
925 
926                                 // look for corresponding cpp file and add it
927                                 std::string file = Rcpp::as<std::string>(		// #nocov
928                                     filePathSansExt(include)) + exts[i];
929 
930                                 exists = fileExists(file);
931                                 if (exists[0]) {
932                                     if (addUniqueDependency(file,
933                                                             pDependencies)) {
934                                         FileInfo fileInfo(file);
935                                         newDependencies.push_back(fileInfo);
936                                     }
937                                 }
938                             }
939                         }
940                     }
941                 }
942             }
943 
944             // look for dependencies recursively
945             for (size_t i = 0; i<newDependencies.size(); i++) {
946                 FileInfo dependency = newDependencies[i];
947                 parseSourceDependencies(dependency.path(), pDependencies);
948             }
949         }
950 
951         // parse the source dependencies from the passed lines
parseSourceDependencies(std::string sourceFile)952         std::vector<FileInfo> parseSourceDependencies(
953                                          std::string sourceFile) {
954 
955             // normalize source file
956             Rcpp::Environment baseEnv = Rcpp::Environment::base_env();
957             Rcpp::Function normalizePath = baseEnv["normalizePath"];
958             sourceFile = Rcpp::as<std::string>(normalizePath(sourceFile, "/"));
959 
960             // parse dependencies
961             std::vector<FileInfo> dependencies;
962             parseSourceDependencies(sourceFile, &dependencies);
963 
964             // remove main source file
965             dependencies.erase(std::remove(dependencies.begin(),	// #nocov
966                                            dependencies.end(),
967                                            FileInfo(sourceFile)),
968                                dependencies.end());
969 
970             return dependencies;
971         }
972 
973         // Parse embedded R code chunks from a file (receives the lines of the
974         // file as a CharcterVector for using with regexec and as a standard
975         // stl vector for traversal/insepection)
parseEmbeddedR(Rcpp::CharacterVector linesVector,const std::deque<std::string> & lines)976         std::vector<std::string> parseEmbeddedR(
977                                         Rcpp::CharacterVector linesVector,
978                                         const std::deque<std::string>& lines) {
979             Rcpp::List matches = regexMatches(linesVector,
980                                               "^\\s*/\\*{3,}\\s*[Rr]\\s*$");
981             bool withinRBlock = false;
982             CommentState commentState;
983             std::vector<std::string> embeddedR;
984 
985             for (int i = 0; i<matches.size(); i++) {
986 
987                 // track comment state
988                 std::string line = lines[i];
989                 commentState.submitLine(line);
990 
991                 // is this a line that begins an R code block?
992                 const Rcpp::CharacterVector match = matches[i];
993                 bool beginRBlock = match.size() > 0;
994 
995                 // check state and do the right thing
996                 if (beginRBlock) {
997                     withinRBlock = true;				// #nocov
998                 }
999                 else if (withinRBlock) {
1000                     if (commentState.inComment())			// #nocov start
1001                         embeddedR.push_back(line);
1002                     else
1003                         withinRBlock = false;				// #nocov end
1004                 }
1005             }
1006 
1007             return embeddedR;
1008         }
1009 
1010     } // anonymous namespace
1011 
1012 
1013     // Generate a type signature for the function with the provided name
1014     // (type signature == function pointer declaration)
signature(const std::string & name) const1015     std::string Function::signature(const std::string& name) const {	// #nocov start
1016 
1017         std::ostringstream ostr;
1018 
1019         ostr << type() << "(*" << name << ")(";
1020 
1021         const std::vector<Argument>& args = arguments();
1022         for (std::size_t i = 0; i<args.size(); i++) {
1023             ostr << args[i].type();
1024             if (i != (args.size()-1))
1025                 ostr << ",";
1026         }
1027         ostr << ")";
1028 
1029         return ostr.str();						// #nocov end
1030     }
1031 
1032 
1033     // Parse attribute parameter from parameter text
Param(const std::string & paramText)1034     Param::Param(const std::string& paramText)
1035     {
1036         // parse out name/value pair if there is one
1037         std::string::size_type pos = paramText.find("=") ;
1038         if ( pos != std::string::npos ) {
1039             // name
1040             name_ = paramText.substr(0, pos);				// #nocov start
1041             trimWhitespace(&name_);
1042             // value
1043             value_ = paramText.substr(pos + 1) ;
1044             trimWhitespace(&value_);
1045             stripQuotes(&value_);					// #nocov end
1046         }
1047         else {
1048             name_ = paramText;
1049             trimWhitespace(&name_);
1050             stripQuotes(&name_);
1051         }
1052     }
1053 
1054     // Check if the attribute has a parameter of a paricular name
paramNamed(const std::string & name) const1055     Param Attribute::paramNamed(const std::string& name) const {
1056         for (std::vector<Param>::const_iterator
1057           it = params_.begin(); it != params_.end(); ++it) {
1058             if (it->name() == name)					// #nocov
1059                 return *it;						// #nocov
1060         }
1061         return Param();
1062     }
1063 
1064     // Type operator <<
operator <<(std::ostream & os,const Type & type)1065     std::ostream& operator<<(std::ostream& os, const Type& type) {
1066         if (!type.empty()) {
1067             if (type.isConst())
1068                 os << "const ";
1069             os << type.name();
1070             if (type.isReference())
1071                 os << "&";
1072         }
1073         return os;
1074     }
1075 
1076     // Print argument
printArgument(std::ostream & os,const Argument & argument,bool printDefault=true)1077     void printArgument(std::ostream& os,
1078                        const Argument& argument,
1079                        bool printDefault = true) {
1080         if (!argument.empty()) {
1081             os << argument.type();
1082             if (!argument.name().empty()) {
1083                 os << " ";
1084                 os << argument.name();
1085                 if (printDefault && !argument.defaultValue().empty())
1086                     os << " = " << argument.defaultValue();		// #nocov
1087             }
1088         }
1089     }
1090 
1091     // Argument operator <<
operator <<(std::ostream & os,const Argument & argument)1092     std::ostream& operator<<(std::ostream& os, const Argument& argument) {// #nocov start
1093         printArgument(os, argument);
1094         return os;							// #nocov end
1095     }
1096 
1097     // Print function
printFunction(std::ostream & os,const Function & function,bool printArgDefaults=true)1098     void printFunction(std::ostream& os,
1099                        const Function& function,
1100                        bool printArgDefaults = true) {
1101 
1102         if (!function.empty()) {
1103             if (!function.type().empty()) {
1104                 os << function.type();
1105                 os << " ";
1106             }
1107             os << function.name();
1108             os << "(";
1109             const std::vector<Argument>& arguments = function.arguments();
1110             for (std::size_t i = 0; i<arguments.size(); i++) {
1111                 printArgument(os, arguments[i], printArgDefaults);
1112                 if (i != (arguments.size()-1))
1113                     os << ", ";
1114             }
1115             os << ")";
1116         }
1117     }
1118 
1119     // Function operator <<
operator <<(std::ostream & os,const Function & function)1120     std::ostream& operator<<(std::ostream& os, const Function& function) {// #nocov start
1121         printFunction(os, function);
1122         return os;
1123     }
1124 
1125     // Param operator <<
operator <<(std::ostream & os,const Param & param)1126     std::ostream& operator<<(std::ostream& os, const Param& param) {
1127         if (!param.empty()) {
1128             os << param.name();
1129             if (!param.value().empty())
1130                 os << "=" << param.value();
1131         }
1132         return os;
1133     }
1134 
1135     // Attribute operator <<
operator <<(std::ostream & os,const Attribute & attribute)1136     std::ostream& operator<<(std::ostream& os, const Attribute& attribute) {
1137         if (!attribute.empty()) {
1138             os << "[[Rcpp::" << attribute.name();
1139             const std::vector<Param>& params = attribute.params();
1140             if (params.size() > 0) {
1141                 os << "(";
1142                 for (std::size_t i = 0; i<params.size(); i++) {
1143                     os << params[i];
1144                     if (i != (params.size()-1))
1145                         os << ",";
1146                 }
1147                 os << ")";
1148             }
1149             os << "]]";
1150 
1151             if (!attribute.function().empty())
1152                 os << " " << attribute.function();
1153         }
1154         return os;							// #nocov end
1155     }
1156 
1157     // Parse the attributes from a source file
SourceFileAttributesParser(const std::string & sourceFile,const std::string & packageName,bool parseDependencies)1158     SourceFileAttributesParser::SourceFileAttributesParser(
1159                                              const std::string& sourceFile,
1160                                              const std::string& packageName,
1161                                              bool parseDependencies)
1162         : sourceFile_(sourceFile), hasPackageInit_(false)
1163     {
1164 
1165         // transform packageName to valid C++ symbol
1166         std::string packageNameCpp = packageName;
1167         std::replace(packageNameCpp.begin(), packageNameCpp.end(), '.', '_');
1168 
1169         // First read the entire file into a std::stringstream so we can check
1170         // it for attributes (we don't want to do any of our more expensive
1171         // processing steps if there are no attributes to parse)
1172         std::stringstream buffer;
1173         readFile(sourceFile_, buffer);
1174         std::string contents = buffer.str();
1175 
1176         // Check for attribute signature
1177         if (contents.find("[[Rcpp::") != std::string::npos ||
1178             contents.find("RCPP_MODULE") != std::string::npos ||
1179             contents.find("R_init_" + packageNameCpp) != std::string::npos) {
1180 
1181             // Now read into a list of strings (which we can pass to regexec)
1182             // First read into a std::deque (which will handle lots of append
1183             // operations efficiently) then copy into an R character vector
1184             std::deque<std::string> lines;
1185             readLines(buffer, &lines);
1186             lines_ = Rcpp::wrap(lines);
1187 
1188             // Scan for attributes
1189             CommentState commentState;
1190             Rcpp::List matches = regexMatches(lines_,
1191                 "^\\s*//\\s*\\[\\[Rcpp::(\\w+)(\\(.*?\\))?\\]\\]\\s*$");
1192             for (int i = 0; i<matches.size(); i++) {
1193 
1194                 // track whether we are in a comment and bail if we are in one
1195                 std::string line = lines[i];
1196                 commentState.submitLine(line);
1197                 if (commentState.inComment())
1198                     continue;
1199 
1200                 // attribute line
1201                 const Rcpp::CharacterVector match = matches[i];
1202                 if (match.size() > 0) {
1203 
1204                     // if the match size isn't 3 then regmatches has not behaved
1205                     // as expected (it should return a vector of either 0 or 3
1206                     // elements). we don't ever expect this to occur but if it
1207                     // does let's not crash
1208                     if (match.size() != 3)
1209                         continue;					// #nocov
1210 
1211                     // add the attribute
1212                     Attribute attr = parseAttribute(
1213                         Rcpp::as<std::vector<std::string> >(match),  i);
1214                     attributes_.push_back(attr);
1215                 }
1216 
1217                 // if it's not an attribute line then it could still be a
1218                 // line of interest (e.g. roxygen comment)
1219                 else {
1220 
1221                     // save roxygen comments
1222                     if (line.find("//'") == 0) {
1223                         std::string roxLine = "#" + line.substr(2);
1224                         roxygenBuffer_.push_back(roxLine);
1225                     }
1226 
1227                     // a non-roxygen line causes us to clear the roxygen buffer
1228                     else if (!roxygenBuffer_.empty()) {
1229                         roxygenChunks_.push_back(roxygenBuffer_);	// #nocov
1230                         roxygenBuffer_.clear();				// #nocov
1231                     }
1232                 }
1233             }
1234 
1235             // Scan for Rcpp modules
1236             commentState.reset();
1237             Rcpp::List modMatches = regexMatches(lines_,
1238                 "^\\s*RCPP_MODULE\\s*\\(\\s*(\\w+)\\s*\\).*$");
1239             for (int i = 0; i<modMatches.size(); i++) {
1240 
1241                 // track whether we are in a comment and bail if we are in one
1242                 std::string line = lines[i];
1243                 commentState.submitLine(line);
1244                 if (commentState.inComment())
1245                     continue;
1246 
1247                 // get the module declaration
1248                 Rcpp::CharacterVector match = modMatches[i];
1249                 if (match.size() > 0) {
1250                     const char * name = match[1];
1251                     modules_.push_back(name);
1252                 }
1253             }
1254 
1255             // Scan for package init function
1256             hasPackageInit_ = false;
1257             commentState.reset();
1258             std::string pkgInit = "R_init_" + packageNameCpp;
1259             Rcpp::List initMatches = regexMatches(lines_, "^[^/]+" + pkgInit + ".*DllInfo.*$");
1260             for (int i = 0; i<initMatches.size(); i++) {
1261 
1262                 // track whether we are in a comment and bail if we are in one
1263                 std::string line = lines[i];
1264                 commentState.submitLine(line);
1265                 if (commentState.inComment())
1266                     continue;
1267 
1268                 // check for a match
1269                 Rcpp::CharacterVector match = initMatches[i];
1270                 if (match.size() > 0) {
1271                     hasPackageInit_ = true;		// #nocov start
1272                     break;
1273                 }					// #nocov end
1274             }
1275 
1276             // Parse embedded R
1277             embeddedR_ = parseEmbeddedR(lines_, lines);
1278 
1279             // Recursively parse dependencies if requested
1280             if (parseDependencies) {
1281 
1282                 // get source dependencies
1283                 sourceDependencies_ = parseSourceDependencies(sourceFile);
1284 
1285                 // parse attributes and modules from each dependent file
1286                 for (size_t i = 0; i<sourceDependencies_.size(); i++) {
1287 
1288                     // perform parse
1289                     std::string dependency = sourceDependencies_[i].path();
1290                     SourceFileAttributesParser parser(dependency, packageName, false);
1291 
1292                     // copy to base attributes (if it's a new attribute)
1293                     for (SourceFileAttributesParser::const_iterator	// #nocov start
1294                             it = parser.begin(); it != parser.end(); ++it) {
1295                         if (std::find(attributes_.begin(),
1296                                       attributes_.end(),
1297                                       *it) == attributes_.end()) {
1298                             attributes_.push_back(*it);			// #nocov end
1299                         }
1300                     }
1301 
1302                     // copy to base modules
1303                     std::copy(parser.modules().begin(),
1304                               parser.modules().end(),
1305                               std::back_inserter(modules_));
1306                 }
1307             }
1308         }
1309     }
1310 
1311     // Parse an attribute from the vector returned by regmatches
parseAttribute(const std::vector<std::string> & match,int lineNumber)1312     Attribute SourceFileAttributesParser::parseAttribute(
1313                                     const std::vector<std::string>& match,
1314                                     int lineNumber) {
1315 
1316         // Attribute name
1317         std::string name = match[1];
1318 
1319         // Warn if this is an unknown attribute
1320         if (!isKnownAttribute(name)) {
1321             attributeWarning("Unrecognized attribute Rcpp::" + name,	// #nocov
1322                              lineNumber);				// #nocov
1323         }
1324 
1325         // Extract params if we've got them
1326         std::vector<Param> params;
1327         std::string paramsText = match[2];
1328         if (!paramsText.empty()) {
1329 
1330             // we know from the regex that it's enclosed in parens so remove
1331             // trim before we do this just in case someone updates the regex
1332             // to allow for whitespace around the call
1333             trimWhitespace(&paramsText);
1334 
1335             paramsText = paramsText.substr(1, paramsText.size()-2);
1336 
1337             // parse the parameters
1338             params = parseParameters(paramsText);
1339         }
1340 
1341         // Extract function signature if this is a function attribute
1342         // and it doesn't appear at the end of the file
1343         Function function;
1344 
1345         // special handling for export and init
1346         if (name == kExportAttribute || name == kInitAttribute) {
1347 
1348             // parse the function (unless we are at the end of the file in
1349             // which case we print a warning)
1350             if ((lineNumber + 1) < lines_.size())
1351                 function = parseFunction(lineNumber + 1);
1352             else
1353                 rcppExportWarning("No function found", lineNumber);	// #nocov
1354 
1355             // validate parameters
1356             for (std::size_t i=0; i<params.size(); i++) {
1357 
1358                 std::string name = params[i].name();			// #nocov start
1359                 std::string value = params[i].value();
1360 
1361                 // un-named parameter that isn't the first parameter
1362                 if (value.empty() && (i > 0)) {
1363                     rcppExportWarning("No value specified for parameter '" +
1364                                       name + "'",
1365                                       lineNumber);
1366                 }
1367                 // parameter that isn't name or rng
1368                 else if (!value.empty() &&
1369                          (name != kExportName) &&
1370                          (name != kExportRng) &&
1371                          (name != kExportInvisible)) {
1372                     rcppExportWarning("Unrecognized parameter '" + name + "'",
1373                                       lineNumber);
1374                 }
1375                 // rng that isn't true or false
1376                 else if (name == kExportRng) {
1377                     if (value != kParamValueFalse &&
1378                         value != kParamValueTrue &&
1379                         value != kParamValueFALSE &&
1380                         value != kParamValueTRUE) {
1381                         rcppExportWarning("rng value must be true or false",
1382                                           lineNumber);
1383                     }
1384                 }
1385                 // invisible that isn't true of false
1386                 else if (name == kExportInvisible) {
1387                     if (value != kParamValueFalse &&
1388                         value != kParamValueTrue &&
1389                         value != kParamValueFALSE &&
1390                         value != kParamValueTRUE) {
1391                         rcppExportWarning("invisible value must be true or false",
1392                                           lineNumber);			// #nocov end
1393                     }
1394                 }
1395             }
1396         }
1397 
1398         // validate interfaces parameter
1399         else if (name == kInterfacesAttribute) {
1400             if (params.empty()) {					// #nocov start
1401                 rcppInterfacesWarning("No interfaces specified", lineNumber);//
1402             }
1403             else {
1404                 for (std::size_t i=0; i<params.size(); i++) {
1405                     std::string param = params[i].name();
1406                     if (param != kInterfaceR && param != kInterfaceCpp) {
1407                         rcppInterfacesWarning(
1408                             "Unknown interface '" + param + "'", lineNumber);
1409                     }							// #nocov end
1410                 }
1411             }
1412 
1413 
1414         }
1415 
1416         // Return attribute
1417         Attribute attribute = Attribute(name, params, function, roxygenBuffer_);
1418         roxygenBuffer_.clear();
1419         return attribute;
1420     }
1421 
1422     // Parse attribute parameters
parseParameters(const std::string & input)1423     std::vector<Param> SourceFileAttributesParser::parseParameters(
1424                                                     const std::string& input) {
1425 
1426         const std::string delimiters(",");
1427 
1428         std::vector<Param> params;
1429         std::string::size_type current;
1430         std::string::size_type next = -1;
1431         do {								// #nocov
1432             next = input.find_first_not_of(delimiters, next + 1);
1433             if (next == std::string::npos)
1434                 break;							// #nocov
1435             next -= 1;
1436             current = next + 1;
1437             next = input.find_first_of(delimiters, current);
1438             params.push_back(Param(input.substr(current, next - current)));
1439         } while(next != std::string::npos);
1440 
1441         return params;
1442     }
1443 
1444     // Parse a function from the specified spot in the source file
parseFunction(size_t lineNumber)1445     Function SourceFileAttributesParser::parseFunction(size_t lineNumber) {
1446 
1447         // Establish the text to parse for the signature
1448         std::string signature = parseSignature(lineNumber);
1449         if (signature.empty()) {
1450             rcppExportNoFunctionFoundWarning(lineNumber);		// #nocov
1451             return Function();						// #nocov
1452         }
1453 
1454         // Start at the end and look for the () that deliniates the arguments
1455         // (bail with an empty result if we can't find them)
1456         std::string::size_type endParenLoc = signature.find_last_of(')');
1457         std::string::size_type beginParenLoc = signature.find_first_of('(');
1458         if (endParenLoc == std::string::npos ||
1459             beginParenLoc == std::string::npos ||
1460             endParenLoc < beginParenLoc) {
1461 
1462             rcppExportNoFunctionFoundWarning(lineNumber);		// #nocov
1463             return Function();						// #nocov
1464         }
1465 
1466         // Find the type and name by scanning backwards for the whitespace that
1467         // delimites the type and name
1468         Type type;
1469         std::string name;
1470         const std::string preambleText = signature.substr(0, beginParenLoc);
1471         for (std::string::const_reverse_iterator
1472             it = preambleText.rbegin(); it != preambleText.rend(); ++it) {
1473             char ch = *it;
1474             if (isWhitespace(ch)) {
1475                 if (!name.empty()) {
1476                     // we are at the break between type and name so we can also
1477                     // extract the type
1478                     std::string typeText;
1479                     while (++it != preambleText.rend())
1480                         typeText.insert(0U, 1U, *it);
1481                     type = parseType(typeText);
1482 
1483                     // break (since we now have the name and the type)
1484                     break;
1485                 }
1486                 else
1487                     continue;					// #nocov
1488             } else {
1489                 name.insert(0U, 1U, ch);
1490             }
1491         }
1492 
1493         // If we didn't find a name then bail
1494         if (name.empty()) {
1495             rcppExportNoFunctionFoundWarning(lineNumber);	// #nocov
1496             return Function();					// #nocov
1497         }
1498 
1499         // If we didn't find a type then bail
1500         if (type.empty()) {					// #nocov start
1501             rcppExportWarning("No function return type found", lineNumber);
1502             return Function();					// #nocov end
1503         }
1504 
1505         // Now scan for arguments
1506         std::vector<Argument> arguments;
1507         std::string argsText = signature.substr(beginParenLoc + 1,
1508                                                  endParenLoc-beginParenLoc-1);
1509         std::vector<std::string> args = parseArguments(argsText);
1510         for (std::vector<std::string>::const_iterator it =
1511                                         args.begin(); it != args.end(); ++it) {
1512 
1513             // Get argument sans whitespace (bail if the arg is empty)
1514             std::string arg = *it;
1515             trimWhitespace(&arg);
1516             if (arg.empty()) {
1517                 // we don't warn here because the compilation will fail anyway
1518                 continue;					// #nocov
1519             }
1520 
1521             // If the argument has an = within it then it has a default value
1522             std::string defaultValue;
1523             std::string::size_type eqPos = arg.find_first_of('=');
1524             if ( (eqPos != std::string::npos) && ((eqPos + 1) < arg.size()) ) {
1525                 defaultValue = arg.substr(eqPos+1);
1526                 trimWhitespace(&defaultValue);
1527                 arg = arg.substr(0, eqPos);
1528                 trimWhitespace(&arg);
1529             }
1530 
1531             // Scan backwards for whitespace to determine where the type ends
1532             // (we go backwards because whitespace is valid inside the type
1533             // identifier but invalid inside the variable name). Note that if
1534             // there is no whitespace we'll end up taking the whole string,
1535             // which allows us to capture a type with no variable (but note
1536             // we'll ultimately fail to parse types with no variable if they
1537             // have embedded whitespace)
1538             std::string::size_type pos = arg.find_last_of(kWhitespaceChars);
1539 
1540             // check for name
1541             std::string name;
1542             if (pos != std::string::npos) {
1543                 // insert whitespace if variables are joint with '&'
1544                 std::string::size_type ref_pos = arg.substr(pos).find_last_of("&");
1545                 if (ref_pos != std::string::npos) {
1546                     pos += ref_pos + 1;				// #nocov
1547                     arg.insert(pos, " ");			// #nocov
1548                 }
1549 
1550                 name = arg.substr(pos);
1551                 trimWhitespace(&name);
1552             }
1553             if (name.empty()) {					// #nocov start
1554                 rcppExportInvalidParameterWarning(arg, lineNumber);
1555                 return Function();				// #nocov end
1556             }
1557 
1558             // check for type string
1559             Type type = parseType(arg.substr(0, pos));
1560             if (type.empty()) {					// #nocov start
1561                 rcppExportInvalidParameterWarning(arg, lineNumber);
1562                 return Function();				// #nocov end
1563             }
1564 
1565             // add argument
1566             arguments.push_back(Argument(name, type, defaultValue));
1567         }
1568 
1569         return Function(type, name, arguments);
1570     }
1571 
1572 
1573     // Parse the text of a function signature from the specified line
parseSignature(size_t lineNumber)1574     std::string SourceFileAttributesParser::parseSignature(size_t lineNumber) {
1575 
1576         // Look for the signature termination ({ or ; not inside quotes)
1577         // on this line and then subsequent lines if necessary
1578         std::string signature;
1579         for (int i = lineNumber; i<lines_.size(); i++) {
1580             std::string line;
1581             line = lines_[i];
1582             bool insideQuotes = false;
1583             char prevChar = 0;
1584             // scan for { or ; not inside quotes
1585             for (size_t c = 0; c < line.length(); ++c) {
1586                 // alias character
1587                 char ch = line.at(c);
1588                 // update quotes state
1589                 if (ch == '"' && prevChar != '\\')
1590                     insideQuotes = !insideQuotes;
1591                 // found signature termination, append and return
1592                 if (!insideQuotes && ((ch == '{') || (ch == ';'))) {
1593                     signature.append(line.substr(0, c));
1594                     return signature;
1595                 }
1596                 // record prev char (used to check for escaped quote i.e. \")
1597                 prevChar = ch;
1598             }
1599 
1600             // if we didn't find a terminator on this line then just append the line
1601             // and move on to the next line
1602             signature.append(line);
1603             signature.push_back(' ');
1604         }
1605 
1606         // Not found
1607         return std::string();						// #nocov
1608     }
1609 
1610 
1611     // Parse arguments from function signature. This is tricky because commas
1612     // are used to delimit arguments but are also valid inside template type
1613     // qualifiers.
parseArguments(const std::string & argText)1614     std::vector<std::string> SourceFileAttributesParser::parseArguments(
1615                                                 const std::string& argText) {
1616 
1617         int templateCount = 0;
1618         int parenCount = 0;
1619         std::string currentArg;
1620         std::vector<std::string> args;
1621         char quote = 0;
1622         bool escaped = false;
1623         typedef std::string::const_iterator it_t;
1624         for (it_t it = argText.begin(); it != argText.end(); ++it) {
1625 
1626             // Store current character
1627             char ch = *it;
1628 
1629             // Ignore quoted strings and character values in single quotes
1630             if ( ! quote && (ch == '"' || ch == '\''))
1631                 quote = ch;
1632             else if (quote && ch == quote && ! escaped)
1633                 quote = 0;
1634 
1635             // Escaped character inside quotes
1636             if (escaped)
1637                 escaped = false;
1638             else if (quote && ch == '\\')
1639                 escaped = true;
1640 
1641             // Detect end of argument declaration
1642             if ( ! quote &&
1643                 (ch == ',') &&
1644                 (templateCount == 0) &&
1645                 (parenCount == 0)) {
1646                 args.push_back(currentArg);
1647                 currentArg.clear();
1648                 continue;
1649             }
1650 
1651             // Append current character if not a space at start
1652             if ( ! currentArg.empty() || ch != ' ')
1653                 currentArg.push_back(ch);
1654 
1655             // Count use of potentially enclosed brackets
1656             if ( ! quote) {
1657                 switch(ch) {
1658                     case '<':
1659                         templateCount++;
1660                         break;
1661                     case '>':
1662                         templateCount--;
1663                         break;
1664                     case '(':				// #nocov start
1665                         parenCount++;
1666                         break;
1667                     case ')':
1668                         parenCount--;
1669                         break;				// #nocov end
1670                 }
1671             }
1672         }
1673 
1674         if (!currentArg.empty())
1675             args.push_back(currentArg);
1676 
1677         return args;
1678     }
1679 
parseType(const std::string & text)1680     Type SourceFileAttributesParser::parseType(const std::string& text) {
1681 
1682         const std::string constQualifier("const");
1683         const std::string referenceQualifier("&");
1684 
1685         // trim whitespace
1686         std::string type = text;
1687         trimWhitespace(&type);
1688 
1689         // check for const and reference
1690         bool isConst = false;
1691         bool isReference = false;
1692         if (type.find(constQualifier) == 0) {
1693             isConst = true;
1694             type.erase(0, constQualifier.length());
1695         }
1696 
1697         // if the type is now empty (because it was detected as only const)
1698         // then this is an invalid state so we bail
1699         if (type.empty())
1700             return Type();					// #nocov
1701 
1702         if (type.find(referenceQualifier) ==
1703             (type.length() - referenceQualifier.length())) {
1704             isReference = true;
1705             type.erase(type.length() - referenceQualifier.length());
1706         }
1707         trimWhitespace(&type);
1708 
1709         // if the type is now empty because of some strange parse then bail
1710         if (type.empty())
1711             return Type();					// #nocov
1712 
1713         return Type(type, isConst, isReference);
1714     }
1715 
1716     // Validation helpers
1717 
isKnownAttribute(const std::string & name) const1718     bool SourceFileAttributesParser::isKnownAttribute(const std::string& name)
1719                                                                         const {
1720         return name == kExportAttribute ||
1721                name == kInitAttribute ||
1722                name == kDependsAttribute ||
1723                name == kPluginsAttribute ||
1724                name == kInterfacesAttribute;
1725     }
1726 
1727     // Print an attribute parsing related warning
attributeWarning(const std::string & message,const std::string & attribute,size_t lineNumber)1728     void SourceFileAttributesParser::attributeWarning(		// #nocov start
1729                                                 const std::string& message,
1730                                                 const std::string& attribute,
1731                                                 size_t lineNumber) {
1732 
1733         // get basename of source file for warning message
1734         Rcpp::Function basename = Rcpp::Environment::base_env()["basename"];
1735         std::string file = Rcpp::as<std::string>(basename(sourceFile_));
1736 
1737         std::ostringstream ostr;
1738         ostr << message;
1739         if (!attribute.empty())
1740             ostr << " for " << attribute << " attribute";
1741         ostr << " at " << file << ":" << lineNumber;
1742 
1743         showWarning(ostr.str());
1744     }
1745 
attributeWarning(const std::string & message,size_t lineNumber)1746     void SourceFileAttributesParser::attributeWarning(
1747                                             const std::string& message,
1748                                             size_t lineNumber) {
1749         attributeWarning(message, "", lineNumber);
1750     }
1751 
rcppExportWarning(const std::string & message,size_t lineNumber)1752     void SourceFileAttributesParser::rcppExportWarning(
1753                                              const std::string& message,
1754                                              size_t lineNumber) {
1755         attributeWarning(message, "Rcpp::export", lineNumber);
1756     }
1757 
rcppExportNoFunctionFoundWarning(size_t lineNumber)1758     void SourceFileAttributesParser::rcppExportNoFunctionFoundWarning(
1759                                                           size_t lineNumber) {
1760         rcppExportWarning("No function found", lineNumber);
1761     }
1762 
rcppExportInvalidParameterWarning(const std::string & param,size_t lineNumber)1763     void SourceFileAttributesParser::rcppExportInvalidParameterWarning(
1764                                                     const std::string& param,
1765                                                     size_t lineNumber) {
1766         rcppExportWarning("Invalid parameter: "
1767                           "'" + param + "'", lineNumber);
1768     }
1769 
rcppInterfacesWarning(const std::string & message,size_t lineNumber)1770     void SourceFileAttributesParser::rcppInterfacesWarning(
1771                                                     const std::string& message,
1772                                                     size_t lineNumber) {
1773         attributeWarning(message + " (valid interfaces are 'r' and 'cpp')",
1774                         "Rcpp::interfaces", lineNumber);
1775     }							// #nocov end
1776 
1777 
1778     // Track /* */ comment state
submitLine(const std::string & line)1779     void CommentState::submitLine(const std::string& line) {
1780         std::size_t pos = 0;
1781         while (pos != std::string::npos) {
1782 
1783             // check for a // which would invalidate any other token found
1784             std::size_t lineCommentPos = line.find("//", pos);
1785 
1786             // look for the next token
1787             std::string token = inComment() ? "*/" : "/*";
1788             pos = line.find(token, pos);
1789 
1790             // process the comment token if found
1791             if (pos != std::string::npos) {
1792 
1793                 // break if the line comment precedes the comment token
1794                 if (lineCommentPos != std::string::npos && lineCommentPos < pos)
1795                     break;				// #nocov
1796 
1797                 inComment_ = !inComment_;
1798                 pos += token.size();
1799             }
1800         }
1801     }
1802 
1803 } // namespace attributes
1804 } // namespace Rcpp
1805 
1806 
1807 /*******************************************************************
1808  * AttributesGen.cpp
1809  *******************************************************************/
1810 
1811 namespace Rcpp {
1812 namespace attributes {
1813 
1814     // constants
1815     namespace {
1816         const char * const kRcppExportsSuffix = "_RcppExports.h";
1817         const char * const kTrySuffix = "_try";
1818     }
1819 
ExportsGenerator(const std::string & targetFile,const std::string & package,const std::string & commentPrefix)1820     ExportsGenerator::ExportsGenerator(const std::string& targetFile,
1821                                        const std::string& package,
1822                                        const std::string& commentPrefix)
1823         : targetFile_(targetFile),
1824           package_(package),
1825           packageCpp_(package),
1826           commentPrefix_(commentPrefix),
1827           hasCppInterface_(false) {
1828 
1829         // read the existing target file if it exists
1830         if (FileInfo(targetFile_).exists()) {
1831             std::ifstream ifs(targetFile_.c_str());		// #nocov start
1832             if (ifs.fail())
1833                 throw Rcpp::file_io_error(targetFile_);
1834             std::stringstream buffer;
1835             buffer << ifs.rdbuf();
1836             existingCode_ = buffer.str();			// #nocov end
1837         }
1838 
1839         std::replace(packageCpp_.begin(), packageCpp_.end(), '.', '_');
1840 
1841         // see if this is safe to overwite and throw if it isn't
1842         if (!isSafeToOverwrite())
1843             throw Rcpp::file_exists(targetFile_);		// #nocov
1844     }
1845 
writeFunctions(const SourceFileAttributes & attributes,bool verbose)1846     void ExportsGenerator::writeFunctions(
1847                                 const SourceFileAttributes& attributes,
1848                                 bool verbose) {
1849 
1850         if (attributes.hasInterface(kInterfaceCpp))
1851             hasCppInterface_ = true;				// #nocov
1852 
1853         doWriteFunctions(attributes, verbose);
1854     }
1855 
1856     // Commit the stream -- is a no-op if the existing code is identical
1857     // to the generated code. Returns true if data was written and false
1858     // if it wasn't (throws exception on io error)
commit(const std::string & preamble)1859     bool ExportsGenerator::commit(const std::string& preamble) {
1860 
1861         // get the generated code
1862         std::string code = codeStream_.str();
1863 
1864         // if there is no generated code AND the exports file does not
1865         // currently exist then do nothing
1866         if (code.empty() && !FileInfo(targetFile_).exists())
1867             return false;					// #nocov
1868 
1869         // write header/preamble
1870         std::ostringstream headerStream;
1871         headerStream << commentPrefix_ << " Generated by using "
1872                      << "Rcpp::compileAttributes()"
1873                      << " -> do not edit by hand" << std::endl;
1874         headerStream << commentPrefix_ << " Generator token: "
1875                      << generatorToken() << std::endl << std::endl;
1876         if (!preamble.empty())
1877             headerStream << preamble;
1878 
1879         // get generated code and only write it if there was a change
1880         std::string generatedCode = headerStream.str() + code;
1881         if (generatedCode != existingCode_) {
1882             // open the file
1883             std::ofstream ofs(targetFile_.c_str(),
1884                               std::ofstream::out | std::ofstream::trunc);
1885             if (ofs.fail())
1886                 throw Rcpp::file_io_error(targetFile_);		// #nocov
1887 
1888             // write generated code and return
1889             ofs << generatedCode;
1890             ofs.close();
1891             return true;
1892         }
1893         else {
1894             return false;					// #nocov
1895         }
1896     }
1897 
1898     // Remove the generated file entirely
remove()1899     bool ExportsGenerator::remove() {
1900         return removeFile(targetFile_);
1901     }
1902 
1903     // Convert a possible dot in package name to underscore as needed for header file
dotNameHelper(const std::string & name) const1904     std::string ExportsGenerator::dotNameHelper(const std::string & name) const {
1905         std::string newname(name);
1906         std::replace(newname.begin(), newname.end(), '.', '_');
1907         return newname;
1908     }
1909 
CppExportsGenerator(const std::string & packageDir,const std::string & package,const std::string & fileSep)1910     CppExportsGenerator::CppExportsGenerator(const std::string& packageDir,
1911                                              const std::string& package,
1912                                              const std::string& fileSep)
1913         : ExportsGenerator(
1914             packageDir + fileSep + "src" +  fileSep + "RcppExports.cpp",
1915             package,
1916             "//")
1917     {
1918     }
1919 
doWriteFunctions(const SourceFileAttributes & attributes,bool verbose)1920     void CppExportsGenerator::doWriteFunctions(
1921                                  const SourceFileAttributes& attributes,
1922                                  bool verbose) {
1923 
1924         // generate functions
1925         generateCpp(ostr(),
1926                     attributes,
1927                     true,
1928                     attributes.hasInterface(kInterfaceCpp),
1929                     packageCppPrefix());
1930 
1931         // track cppExports, signatures, and native routines (we use these
1932         // at the end to generate the ValidateSignature and RegisterCCallable
1933         // functions, and to generate a package init function with native
1934         // routine registration)
1935         for (SourceFileAttributes::const_iterator		// #nocov start
1936                    it = attributes.begin(); it != attributes.end(); ++it) {
1937 
1938             if (it->isExportedFunction()) {
1939 
1940                 // add it to the cpp exports list if we are generating
1941                 // a C++ interface and it's not hidden
1942                 if (attributes.hasInterface(kInterfaceCpp)) {
1943                     Function fun = it->function().renamedTo(it->exportedCppName());
1944                     if (!fun.isHidden())
1945                         cppExports_.push_back(*it);
1946                 }
1947 
1948                 // add it to the native routines list
1949                 nativeRoutines_.push_back(*it);
1950             } else if (it->name() == kInitAttribute) {
1951                 initFunctions_.push_back(*it);
1952             }
1953         }                                               // #nocov end
1954 
1955         // record modules
1956         const std::vector<std::string>& modules = attributes.modules();
1957         modules_.insert(modules_.end(), modules.begin(), modules.end());
1958 
1959         // verbose if requested
1960         if (verbose) {						// #nocov start
1961             Rcpp::Rcout << "Exports from " << attributes.sourceFile() << ":"
1962                         << std::endl;
1963             for (std::vector<Attribute>::const_iterator
1964                     it = attributes.begin(); it != attributes.end(); ++it) {
1965                 if (it->isExportedFunction())
1966                     Rcpp::Rcout << "   " << it->function() << std::endl;
1967             }
1968             Rcpp::Rcout << std::endl;				// #nocov end
1969         }
1970     }
1971 
writeEnd(bool hasPackageInit)1972     void CppExportsGenerator::writeEnd(bool hasPackageInit)
1973     {
1974         // generate a function that can be used to validate exported
1975         // functions and their signatures prior to looking up with
1976         // GetCppCallable (otherwise inconsistent signatures between
1977         // client and library would cause a crash)
1978         if (hasCppInterface()) {
1979 
1980             ostr() << std::endl;				// #nocov start
1981             ostr() << "// validate"
1982                    << " (ensure exported C++ functions exist before "
1983                    << "calling them)" << std::endl;
1984             ostr() << "static int " << exportValidationFunctionRegisteredName()
1985                    << "(const char* sig) { " << std::endl;
1986             ostr() << "    static std::set<std::string> signatures;"
1987                    << std::endl;
1988             ostr() << "    if (signatures.empty()) {" << std::endl;
1989 
1990             for (std::size_t i=0;i<cppExports_.size(); i++) {
1991                 const Attribute& attr = cppExports_[i];
1992                 ostr() << "        signatures.insert(\""
1993                        << attr.function().signature(attr.exportedName())
1994                        << "\");" << std::endl;
1995             }
1996             ostr() << "    }" << std::endl;
1997             ostr() << "    return signatures.find(sig) != signatures.end();"
1998                    << std::endl;
1999             ostr() << "}" << std::endl;
2000 
2001             // generate a function that will register all of our C++
2002             // exports as C-callable from other packages
2003             ostr() << std::endl;
2004             ostr() << "// registerCCallable (register entry points for "
2005                       "exported C++ functions)" << std::endl;
2006             ostr() << "RcppExport SEXP " << registerCCallableExportedName()
2007                    << "() { " << std::endl;
2008             for (std::size_t i=0;i<cppExports_.size(); i++) {
2009                 const Attribute& attr = cppExports_[i];
2010                 ostr() << registerCCallable(
2011                               4,
2012                               attr.exportedName(),
2013                               attr.function().name() + kTrySuffix);
2014                 ostr() << std::endl;
2015             }
2016             ostr() << registerCCallable(4,
2017                                         exportValidationFunction(),
2018                                         exportValidationFunction());
2019             ostr() << std::endl;
2020             ostr() << "    return R_NilValue;" << std::endl;
2021             ostr() << "}" << std::endl;
2022          }
2023 
2024          // write native routines
2025          if (!hasPackageInit && (!nativeRoutines_.empty() || !modules_.empty() || !initFunctions_.empty())) {
2026 
2027             // build list of routines we will register
2028             std::vector<std::string> routineNames;
2029             std::vector<std::size_t> routineArgs;
2030             for (std::size_t i=0;i<nativeRoutines_.size(); i++) {
2031                 const Attribute& attr = nativeRoutines_[i];
2032                 routineNames.push_back(packageCppPrefix() + "_" + attr.function().name());
2033                 routineArgs.push_back(attr.function().arguments().size());
2034             }
2035             std::string kRcppModuleBoot = "_rcpp_module_boot_";
2036             for (std::size_t i=0;i<modules_.size(); i++) {
2037                 routineNames.push_back(kRcppModuleBoot + modules_[i]);
2038                 routineArgs.push_back(0);
2039             }
2040             if (hasCppInterface()) {
2041                 routineNames.push_back(registerCCallableExportedName());
2042                 routineArgs.push_back(0);
2043             }
2044 
2045             // see if there are additional registrations to perform
2046             Rcpp::Function extraRoutinesFunc = Environment::namespace_env("Rcpp")[".extraRoutineRegistrations"];
2047             List extraRoutines = extraRoutinesFunc(targetFile(), routineNames);
2048             std::vector<std::string> declarations = extraRoutines["declarations"];
2049             std::vector<std::string> callEntries = extraRoutines["call_entries"];
2050 
2051             // add declarations for modules
2052             for (std::size_t i=0;i<modules_.size(); i++) {
2053                 declarations.push_back("RcppExport SEXP " + kRcppModuleBoot + modules_[i] + "();");
2054             }
2055 
2056             // generate declarations
2057             if (declarations.size() > 0) {
2058                 ostr() << std::endl;
2059                 for (std::size_t i = 0; i<declarations.size(); i++)
2060                     ostr() << declarations[i] << std::endl;
2061             }
2062 
2063             // generate registration code
2064             ostr() << std::endl;
2065             ostr() << "static const R_CallMethodDef CallEntries[] = {" << std::endl;
2066             for (std::size_t i=0;i<routineNames.size(); i++) {
2067                 ostr() << "    {\"" << routineNames[i] <<  "\", " <<
2068                     "(DL_FUNC) &" << routineNames[i] << ", " <<
2069                         routineArgs[i] <<  "}," << std::endl;
2070             }
2071             if (callEntries.size() > 0) {
2072                 for (std::size_t i = 0; i<callEntries.size(); i++)
2073                     ostr() << callEntries[i] << std::endl;
2074             }
2075             ostr() << "    {NULL, NULL, 0}" << std::endl;
2076             ostr() << "};" << std::endl;
2077 
2078             ostr() << std::endl;
2079 
2080             // write prototypes for init functions
2081             for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2082                 const Function& function = initFunctions_[i].function();
2083                 printFunction(ostr(), function, false);
2084                 ostr() << ";" << std::endl;
2085             }
2086 
2087             ostr() << "RcppExport void R_init_" << packageCpp() << "(DllInfo *dll) {" << std::endl;
2088             ostr() << "    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);" << std::endl;
2089             ostr() << "    R_useDynamicSymbols(dll, FALSE);" << std::endl;
2090             // call init functions
2091             for (std::size_t i = 0; i<initFunctions_.size(); i++) {
2092                 const Function& function = initFunctions_[i].function();
2093                 ostr() << "    " << function.name() << "(dll);" << std::endl;
2094             }
2095             ostr() << "}" << std::endl;
2096          }
2097 
2098          // warn if both a hand-written package init function and Rcpp::init are used
2099          if (hasPackageInit && !initFunctions_.empty()) {
2100             showWarning("[[Rcpp::init]] attribute used in a package with an explicit "
2101                         "R_init function (Rcpp::init functions will not be called)");
2102          }
2103     }
2104 
registerCCallable(size_t indent,const std::string & exportedName,const std::string & name) const2105     std::string CppExportsGenerator::registerCCallable(
2106                                         size_t indent,
2107                                         const std::string& exportedName,
2108                                         const std::string& name) const {
2109         std::ostringstream ostr;
2110         std::string indentStr(indent, ' ');
2111         ostr <<  indentStr << "R_RegisterCCallable(\"" << package() << "\", "
2112               << "\"" << packageCppPrefix() << "_" << exportedName << "\", "
2113               << "(DL_FUNC)" << packageCppPrefix() << "_" << name << ");";
2114         return ostr.str();					// #nocov end
2115     }
2116 
commit(const std::vector<std::string> & includes)2117     bool CppExportsGenerator::commit(const std::vector<std::string>& includes) {
2118 
2119         // includes
2120         std::ostringstream ostr;
2121         if (!includes.empty()) {
2122             for (std::size_t i=0;i<includes.size(); i++)
2123                 ostr << includes[i] << std::endl;
2124         }
2125         if (hasCppInterface()) {
2126             ostr << "#include <string>" << std::endl;		// #nocov
2127             ostr << "#include <set>" << std::endl;		// #nocov
2128         }
2129         ostr << std::endl;
2130 
2131         // always bring in Rcpp
2132         ostr << "using namespace Rcpp;" << std::endl << std::endl;
2133         // initialize references to global Rostreams
2134         initializeGlobals(ostr);
2135 
2136         // commit with preamble
2137         return ExportsGenerator::commit(ostr.str());
2138     }
2139 
CppExportsIncludeGenerator(const std::string & packageDir,const std::string & package,const std::string & fileSep)2140     CppExportsIncludeGenerator::CppExportsIncludeGenerator(
2141                                             const std::string& packageDir,
2142                                             const std::string& package,
2143                                             const std::string& fileSep)
2144         : ExportsGenerator(
2145             packageDir +  fileSep + "inst" +  fileSep + "include" +
2146             fileSep + dotNameHelper(package) + kRcppExportsSuffix,
2147             package,
2148             "//")
2149     {
2150         includeDir_ = packageDir +  fileSep + "inst" +  fileSep + "include";
2151     }
2152 
writeBegin()2153     void CppExportsIncludeGenerator::writeBegin() {
2154 
2155         ostr() << "namespace " << packageCpp() << " {"
2156                << std::endl << std::endl;
2157 
2158         // Import Rcpp into this namespace. This allows declarations to
2159         // be written without fully qualifying all Rcpp types. The only
2160         // negative side-effect is that when this package's namespace
2161         // is imported it will also pull in Rcpp. However since this is
2162         // opt-in and represents a general desire to do namespace aliasing
2163         // this seems okay
2164         ostr() << "    using namespace Rcpp;" << std::endl << std::endl;
2165 
2166         // Write our export validation helper function. Putting it in
2167         // an anonymous namespace will hide it from callers and give
2168         // it per-translation unit linkage
2169         ostr() << "    namespace {" << std::endl;
2170         ostr() << "        void validateSignature(const char* sig) {"
2171                << std::endl;
2172         ostr() << "            Rcpp::Function require = "
2173                << "Rcpp::Environment::base_env()[\"require\"];"
2174                << std::endl;
2175         ostr() << "            require(\"" << package() << "\", "
2176                << "Rcpp::Named(\"quietly\") = true);"
2177                << std::endl;
2178 
2179         std::string validate = "validate";
2180         std::string fnType = "Ptr_" + validate;
2181         ostr() << "            typedef int(*" << fnType << ")(const char*);"
2182                << std::endl;
2183 
2184         std::string ptrName = "p_" + validate;
2185         ostr() << "            static " << fnType << " " << ptrName << " = "
2186                << "(" << fnType << ")" << std::endl
2187                << "                "
2188                << getCCallable(exportValidationFunctionRegisteredName())
2189                << ";" << std::endl;
2190         ostr() << "            if (!" << ptrName << "(sig)) {" << std::endl;
2191         ostr() << "                throw Rcpp::function_not_exported("
2192                << std::endl
2193                << "                    "
2194                << "\"C++ function with signature '\" + std::string(sig) + \"' not found in " << package()
2195                << "\");" << std::endl;
2196         ostr() << "            }" << std::endl;
2197         ostr() << "        }" << std::endl;
2198 
2199         ostr() << "    }" << std::endl << std::endl;
2200     }
2201 
doWriteFunctions(const SourceFileAttributes & attributes,bool)2202     void CppExportsIncludeGenerator::doWriteFunctions(
2203                                     const SourceFileAttributes& attributes,
2204                                     bool) {
2205 
2206         // don't write anything if there is no C++ interface
2207         if (!attributes.hasInterface(kInterfaceCpp))
2208             return;
2209 
2210         for(std::vector<Attribute>::const_iterator		// #nocov start
2211             it = attributes.begin(); it != attributes.end(); ++it) {
2212 
2213             if (it->isExportedFunction()) {
2214 
2215                 Function function =
2216                     it->function().renamedTo(it->exportedCppName());
2217 
2218                 // if it's hidden then don't generate a C++ interface
2219                 if (function.isHidden())
2220                     continue;
2221 
2222                 ostr() << "    inline " << function << " {"
2223                         << std::endl;
2224 
2225                 std::string fnType = "Ptr_" + function.name();
2226                 ostr() << "        typedef SEXP(*" << fnType << ")(";
2227                 for (size_t i=0; i<function.arguments().size(); i++) {
2228                     ostr() << "SEXP";
2229                     if (i != (function.arguments().size()-1))
2230                         ostr() << ",";
2231                 }
2232                 ostr() << ");" << std::endl;
2233 
2234                 std::string ptrName = "p_" + function.name();
2235                 ostr() << "        static " << fnType << " "
2236                        << ptrName << " = NULL;"
2237                        << std::endl;
2238                 ostr() << "        if (" << ptrName << " == NULL) {"
2239                        << std::endl;
2240                 ostr() << "            validateSignature"
2241                        << "(\"" << function.signature() << "\");"
2242                        << std::endl;
2243                 ostr() << "            " << ptrName << " = "
2244                        << "(" << fnType << ")"
2245                        << getCCallable(packageCppPrefix() + "_" + function.name()) << ";"
2246                        << std::endl;
2247                 ostr() << "        }" << std::endl;
2248                 ostr() << "        RObject rcpp_result_gen;" << std::endl;
2249                 ostr() << "        {" << std::endl;
2250                 if (it->rng())
2251                     ostr() << "            RNGScope RCPP_rngScope_gen;" << std::endl;
2252                 ostr() << "            rcpp_result_gen = " << ptrName << "(";
2253 
2254                 const std::vector<Argument>& args = function.arguments();
2255                 for (std::size_t i = 0; i<args.size(); i++) {
2256                     ostr() << "Shield<SEXP>(Rcpp::wrap(" << args[i].name() << "))";
2257                     if (i != (args.size()-1))
2258                         ostr() << ", ";
2259                 }
2260 
2261                 ostr() << ");" << std::endl;
2262                 ostr() << "        }" << std::endl;
2263 
2264                 ostr() << "        if (rcpp_result_gen.inherits(\"interrupted-error\"))"
2265                        << std::endl
2266                        << "            throw Rcpp::internal::InterruptedException();"
2267                        << std::endl;
2268                 ostr() << "        if (Rcpp::internal::isLongjumpSentinel(rcpp_result_gen))"
2269                        << std::endl
2270                        << "            throw Rcpp::LongjumpException(rcpp_result_gen);"
2271                        << std::endl;
2272                 ostr() << "        if (rcpp_result_gen.inherits(\"try-error\"))"
2273                        << std::endl
2274                        << "            throw Rcpp::exception(Rcpp::as<std::string>("
2275                        << "rcpp_result_gen).c_str());"
2276                        << std::endl;
2277                 if (!function.type().isVoid()) {
2278                     ostr() << "        return Rcpp::as<" << function.type() << " >"
2279                            << "(rcpp_result_gen);" << std::endl;
2280                 }
2281 
2282                 ostr() << "    }" << std::endl << std::endl;	// #nocov end
2283             }
2284         }
2285     }
2286 
writeEnd(bool)2287     void CppExportsIncludeGenerator::writeEnd(bool) {
2288         ostr() << "}" << std::endl;
2289         ostr() << std::endl;
2290         ostr() << "#endif // " << getHeaderGuard() << std::endl;
2291     }
2292 
commit(const std::vector<std::string> & includes)2293     bool CppExportsIncludeGenerator::commit(
2294                                     const std::vector<std::string>& includes) {
2295 
2296         if (hasCppInterface()) {
2297 
2298             // create the include dir if necessary
2299             createDirectory(includeDir_);			// #nocov start
2300 
2301             // generate preamble
2302             std::ostringstream ostr;
2303 
2304             // header guard
2305             std::string guard = getHeaderGuard();
2306             ostr << "#ifndef " << guard << std::endl;
2307             ostr << "#define " << guard << std::endl << std::endl;
2308 
2309             // includes
2310             if (!includes.empty()) {
2311                 for (std::size_t i=0;i<includes.size(); i++)
2312                 {
2313                     // some special processing is required here. we exclude
2314                     // the package header file (since it includes this file)
2315                     // and we transorm _types includes into local includes
2316                     std::string preamble = "#include \"../inst/include/";
2317                     std::string pkgInclude = preamble + packageCpp() + ".h\"";
2318                     if (includes[i] == pkgInclude)
2319                         continue;
2320 
2321                     // check for _types
2322                     std::string typesInclude = preamble + packageCpp() + "_types.h";
2323                     if (includes[i].find(typesInclude) != std::string::npos)
2324                     {
2325                         std::string include = "#include \"" +
2326                                       includes[i].substr(preamble.length());
2327                         ostr << include << std::endl;
2328                     }
2329                     else
2330                     {
2331                         ostr << includes[i] << std::endl;
2332                     }
2333                 }
2334                 ostr << std::endl;
2335             }
2336 
2337             // commit with preamble
2338             return ExportsGenerator::commit(ostr.str());		// #nocov end
2339         }
2340         else {
2341             return ExportsGenerator::remove();
2342         }
2343     }
2344 
getCCallable(const std::string & function) const2345     std::string CppExportsIncludeGenerator::getCCallable(
2346                                         const std::string& function) const {
2347         std::ostringstream ostr;
2348         ostr << "R_GetCCallable"
2349              << "(\"" << package() << "\", "
2350              << "\"" << function << "\")";
2351         return ostr.str();
2352     }
2353 
getHeaderGuard() const2354     std::string CppExportsIncludeGenerator::getHeaderGuard() const {
2355         return "RCPP_" + packageCpp() + "_RCPPEXPORTS_H_GEN_";
2356     }
2357 
CppPackageIncludeGenerator(const std::string & packageDir,const std::string & package,const std::string & fileSep)2358     CppPackageIncludeGenerator::CppPackageIncludeGenerator(
2359                                             const std::string& packageDir,
2360                                             const std::string& package,
2361                                             const std::string& fileSep)
2362         : ExportsGenerator(
2363             packageDir +  fileSep + "inst" +  fileSep + "include" +
2364             fileSep + dotNameHelper(package) + ".h",
2365             package,
2366             "//")
2367     {
2368         includeDir_ = packageDir +  fileSep + "inst" +  fileSep + "include";
2369     }
2370 
writeEnd(bool)2371     void CppPackageIncludeGenerator::writeEnd(bool) {
2372         if (hasCppInterface()) {
2373             // header guard
2374             std::string guard = getHeaderGuard();			// #nocov start
2375             ostr() << "#ifndef " << guard << std::endl;
2376             ostr() << "#define " << guard << std::endl << std::endl;
2377             ostr() << "#include \"" << packageCpp() << kRcppExportsSuffix
2378                    << "\"" << std::endl;
2379 
2380             ostr() << std::endl;
2381             ostr() << "#endif // " << getHeaderGuard() << std::endl;	// #nocov end
2382         }
2383     }
2384 
commit(const std::vector<std::string> &)2385     bool CppPackageIncludeGenerator::commit(const std::vector<std::string>&) {
2386         if (hasCppInterface()) {
2387 
2388             // create the include dir if necessary
2389             createDirectory(includeDir_);				// #nocov
2390 
2391             // commit
2392             return ExportsGenerator::commit();				// #nocov
2393         }
2394         else {
2395             return ExportsGenerator::remove();
2396         }
2397     }
2398 
getHeaderGuard() const2399     std::string CppPackageIncludeGenerator::getHeaderGuard() const {	// #nocov
2400         return "RCPP_" + packageCpp() + "_H_GEN_";			// #nocov
2401     }
2402 
RExportsGenerator(const std::string & packageDir,const std::string & package,bool registration,const std::string & fileSep)2403     RExportsGenerator::RExportsGenerator(const std::string& packageDir,
2404                                          const std::string& package,
2405                                          bool registration,
2406                                          const std::string& fileSep)
2407         : ExportsGenerator(
2408             packageDir + fileSep + "R" +  fileSep + "RcppExports.R",
2409             package,
2410             "#"),
2411           registration_(registration)
2412     {
2413     }
2414 
doWriteFunctions(const SourceFileAttributes & attributes,bool)2415     void RExportsGenerator::doWriteFunctions(
2416                                         const SourceFileAttributes& attributes,
2417                                         bool) {
2418         // write standalone roxygen chunks
2419         const std::vector<std::vector<std::string> >& roxygenChunks =
2420                                                     attributes.roxygenChunks();
2421         for (std::size_t i = 0; i<roxygenChunks.size(); i++) {
2422             const std::vector<std::string>& chunk = roxygenChunks[i];	// #nocov start
2423             for (std::size_t l = 0; l < chunk.size(); l++)
2424                 ostr() << chunk[l] << std::endl;
2425             ostr() << "NULL" << std::endl << std::endl;			// #nocov end
2426         }
2427 
2428         // write exported functions
2429         if (attributes.hasInterface(kInterfaceR)) {
2430             // process each attribute
2431             for(std::vector<Attribute>::const_iterator
2432                 it = attributes.begin(); it != attributes.end(); ++it) {
2433 
2434                 // alias the attribute and function (bail if not export)
2435                 const Attribute& attribute = *it;
2436                 if (!attribute.isExportedFunction())
2437                     continue;					// #nocov
2438                 const Function& function = attribute.function();
2439 
2440                 // print roxygen lines
2441                 for (size_t i=0; i<attribute.roxygen().size(); i++)
2442                     ostr() << attribute.roxygen()[i] << std::endl; // #nocov
2443 
2444                 // build the parameter list
2445                 std::string args = generateRArgList(function);
2446 
2447                 // determine the function name
2448                 std::string name = attribute.exportedName();
2449 
2450                 // determine if return invisible
2451                 bool  isInvisibleOrVoid = function.type().isVoid() || attribute.invisible();
2452 
2453                 // write the function
2454                 ostr() << name << " <- function(" << args << ") {"
2455                        << std::endl;
2456                 ostr() << "    ";
2457                 if (isInvisibleOrVoid)
2458                     ostr() << "invisible(";			// #nocov
2459                 ostr() << ".Call(";
2460                 if (!registration_)
2461                     ostr() << "'";				// #nocov
2462                 else
2463                     ostr() << "`";
2464                 ostr() << packageCppPrefix() << "_" << function.name();
2465                 if (!registration_)
2466                     ostr() << "', " << "PACKAGE = '" << package() << "'";  // #nocov
2467                 else
2468                     ostr() << "`";
2469 
2470                 // add arguments
2471                 const std::vector<Argument>& arguments = function.arguments();
2472                 for (size_t i = 0; i<arguments.size(); i++)
2473                     ostr() << ", " << arguments[i].name();	// #nocov
2474                 ostr() << ")";
2475                 if (isInvisibleOrVoid)
2476                     ostr() << ")";				// #nocov
2477                 ostr() << std::endl;
2478 
2479                 ostr() << "}" << std::endl << std::endl;
2480             }
2481         }
2482     }
2483 
writeEnd(bool)2484     void RExportsGenerator::writeEnd(bool) {
2485         if (hasCppInterface()) {				// #nocov start
2486              // register all C-callable functions
2487             ostr() << "# Register entry points for exported C++ functions"
2488                    << std::endl;
2489             ostr() << "methods::setLoadAction(function(ns) {" << std::endl;
2490             ostr() << "    .Call('" << registerCCallableExportedName()
2491                    << "', PACKAGE = '" << package() << "')"
2492                    << std::endl << "})" << std::endl;		// #nocov end
2493         }
2494     }
2495 
commit(const std::vector<std::string> &)2496     bool RExportsGenerator::commit(const std::vector<std::string>&) {
2497         return ExportsGenerator::commit();
2498     }
2499 
~ExportsGenerators()2500     ExportsGenerators::~ExportsGenerators() {
2501         try {
2502             for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2503                 delete *it;
2504             generators_.clear();
2505         }
2506         catch(...) {}
2507     }
2508 
add(ExportsGenerator * pGenerator)2509     void ExportsGenerators::add(ExportsGenerator* pGenerator) {
2510         generators_.push_back(pGenerator);
2511     }
2512 
writeBegin()2513     void ExportsGenerators::writeBegin() {
2514         for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2515             (*it)->writeBegin();
2516     }
2517 
writeFunctions(const SourceFileAttributes & attributes,bool verbose)2518     void ExportsGenerators::writeFunctions(
2519                                 const SourceFileAttributes& attributes,
2520                                 bool verbose) {
2521         for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2522             (*it)->writeFunctions(attributes, verbose);
2523     }
2524 
writeEnd(bool hasPackageInit)2525     void ExportsGenerators::writeEnd(bool hasPackageInit) {
2526         for(Itr it = generators_.begin(); it != generators_.end(); ++it)
2527             (*it)->writeEnd(hasPackageInit);
2528     }
2529 
2530     // Commit and return a list of the files that were updated
commit(const std::vector<std::string> & includes)2531     std::vector<std::string> ExportsGenerators::commit(
2532                             const std::vector<std::string>& includes) {
2533 
2534         std::vector<std::string> updated;
2535 
2536         for(Itr it = generators_.begin(); it != generators_.end(); ++it) {
2537             if ((*it)->commit(includes))
2538                 updated.push_back((*it)->targetFile());
2539         }
2540 
2541         return updated;
2542     }
2543 
2544     // Remove and return a list of files that were removed
remove()2545     std::vector<std::string> ExportsGenerators::remove() {	// #nocov start
2546         std::vector<std::string> removed;
2547         for(Itr it = generators_.begin(); it != generators_.end(); ++it) {
2548             if ((*it)->remove())
2549                 removed.push_back((*it)->targetFile());
2550         }
2551         return removed;
2552     }
2553 
2554 
2555     // Helpers for converting C++  default arguments to R default arguments
2556     namespace {
2557 
2558         // convert a C++ numeric argument to an R argument value
2559         // (returns empty string if no conversion is possible)
cppNumericArgToRArg(const std::string & type,const std::string & cppArg)2560         std::string cppNumericArgToRArg(const std::string& type,
2561                                         const std::string& cppArg) {
2562             // check for a number
2563             double num;
2564             std::stringstream argStream(cppArg);
2565             if ((argStream >> num)) {
2566 
2567                 // L suffix means return the value literally
2568                 if (!argStream.eof()) {
2569                     std::string suffix;
2570                     argStream >> suffix;
2571                     if (argStream.eof() && suffix == "L")
2572                         return cppArg;
2573                 }
2574 
2575                 // no decimal and the type isn't explicitly double or
2576                 // float means integer
2577                 if (cppArg.find('.') == std::string::npos &&
2578                     type != "double" && type != "float")
2579                     return cppArg + "L";
2580 
2581                 // otherwise return arg literally
2582                 else
2583                     return cppArg;
2584             }
2585             else {
2586                 return std::string();
2587             }
2588         }
2589 
2590         // convert a C++ ::create style argument value to an R argument
2591         // value (returns empty string if no conversion is possible)
cppCreateArgToRArg(const std::string & cppArg)2592         std::string cppCreateArgToRArg(const std::string& cppArg) {
2593 
2594             std::string create = "::create";
2595             size_t createLoc = cppArg.find(create);
2596             if (createLoc == std::string::npos ||
2597                 ((createLoc + create.length()) >= cppArg.size())) {
2598                 return std::string();
2599             }
2600 
2601             std::string type = cppArg.substr(0, createLoc);
2602             std::string rcppScope = "Rcpp::";
2603             size_t rcppLoc = type.find(rcppScope);
2604             if (rcppLoc == 0 && type.size() > rcppScope.length())
2605                 type = type.substr(rcppScope.length());
2606 
2607             std::string args = cppArg.substr(createLoc + create.length());
2608             if (type == "CharacterVector")
2609                 return "as.character( c" + args + ")";
2610             if (type == "IntegerVector")
2611                 return "as.integer( c" + args + ")";
2612             if (type == "NumericVector")
2613                 return "as.numeric( c" + args + ")";
2614             if (type == "LogicalVector")
2615                 return "as.logical( c" + args + ")";
2616 
2617             return std::string();
2618         }
2619 
2620         // convert a C++ Matrix to an R argument (returns empty string
2621         // if no conversion possible)
cppMatrixArgToRArg(const std::string & cppArg)2622         std::string cppMatrixArgToRArg(const std::string& cppArg) {
2623 
2624             // look for Matrix
2625             std::string matrix = "Matrix";
2626             size_t matrixLoc = cppArg.find(matrix);
2627             if (matrixLoc == std::string::npos ||
2628                 ((matrixLoc + matrix.length()) >= cppArg.size())) {
2629                 return std::string();
2630             }
2631 
2632             std::string args = cppArg.substr(matrixLoc + matrix.length());
2633             return "matrix" + args;				// #nocov end
2634         }
2635 
2636         // convert a C++ literal to an R argument (returns empty string
2637         // if no conversion possible)
cppLiteralArgToRArg(const std::string & cppArg)2638         std::string cppLiteralArgToRArg(const std::string& cppArg) {
2639             if (cppArg == "true")
2640                 return "TRUE";
2641             else if (cppArg == "false")
2642                 return "FALSE";
2643             else if (cppArg == "R_NilValue")
2644                 return "NULL";
2645             else if (cppArg == "NA_STRING")			// #nocov start
2646                 return "NA_character_";
2647             else if (cppArg == "NA_INTEGER")
2648                 return "NA_integer_";
2649             else if (cppArg == "NA_LOGICAL")
2650                 return "NA_integer_";
2651             else if (cppArg == "NA_REAL")
2652                 return "NA_real_";
2653             else
2654                 return std::string();
2655         }
2656 
2657         // convert an Rcpp container constructor to an R argument
2658         // (returns empty string if no conversion possible)
cppConstructorArgToRArg(const std::string & cppArg)2659         std::string cppConstructorArgToRArg(const std::string& cppArg) {
2660 
2661             // map Rcpp containers to R default initializers
2662             static std::map<std::string, std::string> RcppContainerToR;
2663             RcppContainerToR.insert(std::make_pair("NumericVector", "numeric"));
2664             RcppContainerToR.insert(std::make_pair("DoubleVector", "numeric"));
2665             RcppContainerToR.insert(std::make_pair("CharacterVector", "character"));
2666             RcppContainerToR.insert(std::make_pair("IntegerVector", "integer"));
2667             RcppContainerToR.insert(std::make_pair("LogicalVector", "logical"));
2668             RcppContainerToR.insert(std::make_pair("ComplexVector", "complex"));
2669 
2670             // for each entry in the map above, see if we find it; if we do,
2671             // return the R version
2672             typedef std::map<std::string, std::string>::const_iterator Iterator;
2673             for (Iterator it = RcppContainerToR.begin(); it != RcppContainerToR.end(); ++it) {
2674                 size_t loc = cppArg.find(it->first);
2675                 if (loc != std::string::npos) {
2676                     return it->second + cppArg.substr(it->first.size(), std::string::npos);
2677                 }
2678             }
2679 
2680             return std::string();				// #nocov end
2681 
2682         }
2683 
2684         // convert a C++ argument value to an R argument value (returns empty
2685         // string if no conversion is possible)
cppArgToRArg(const std::string & type,const std::string & cppArg)2686         std::string cppArgToRArg(const std::string& type,
2687                                  const std::string& cppArg) {
2688 
2689             // try for quoted string
2690             if (isQuoted(cppArg))
2691                 return cppArg;
2692 
2693             // try for literal
2694             std::string rArg = cppLiteralArgToRArg(cppArg);
2695             if (!rArg.empty())
2696                 return rArg;
2697 
2698             // try for a create arg
2699             rArg = cppCreateArgToRArg(cppArg);	    		// #nocov start
2700             if (!rArg.empty())
2701                 return rArg;
2702 
2703             // try for a matrix arg
2704             rArg = cppMatrixArgToRArg(cppArg);
2705             if (!rArg.empty())
2706                 return rArg;
2707 
2708             // try for a numeric arg
2709             rArg = cppNumericArgToRArg(type, cppArg);
2710             if (!rArg.empty())
2711                 return rArg;
2712 
2713             // try for a constructor arg
2714             rArg = cppConstructorArgToRArg(cppArg);
2715             if (!rArg.empty())
2716                 return rArg;
2717 
2718             // couldn't parse the arg
2719             return std::string();				// #nocov end
2720         }
2721 
2722     } // anonymous namespace
2723 
2724     // Generate an R argument list for a function
generateRArgList(const Function & function)2725     std::string generateRArgList(const Function& function) {
2726         std::ostringstream argsOstr;
2727         const std::vector<Argument>& arguments = function.arguments();
2728         for (size_t i = 0; i<arguments.size(); i++) {
2729             const Argument& argument = arguments[i];
2730             argsOstr << argument.name();
2731             if (!argument.defaultValue().empty()) {
2732                 std::string rArg = cppArgToRArg(argument.type().name(),
2733                                                 argument.defaultValue());
2734                 if (!rArg.empty()) {
2735                     argsOstr << " = " << rArg;
2736                 } else {
2737                     showWarning("Unable to parse C++ default value '" +		// #nocov start
2738                                 argument.defaultValue() + "' for argument "+
2739                                 argument.name() + " of function " +
2740                                 function.name());				// #nocov end
2741                 }
2742             }
2743 
2744             if (i != (arguments.size()-1))
2745                 argsOstr << ", ";
2746         }
2747         return argsOstr.str();
2748     }
2749 
2750     // Generate the C++ code required to initialize global objects
initializeGlobals(std::ostream & ostr)2751     void initializeGlobals(std::ostream& ostr) {
2752         ostr << "#ifdef RCPP_USE_GLOBAL_ROSTREAM" << std::endl;
2753         ostr << "Rcpp::Rostream<true>&  Rcpp::Rcout = Rcpp::Rcpp_cout_get();";
2754         ostr << std::endl;
2755         ostr << "Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();";
2756         ostr << std::endl;
2757         ostr << "#endif" << std::endl << std::endl;
2758     }
2759 
2760     // Generate the C++ code required to make [[Rcpp::export]] functions
2761     // available as C symbols with SEXP parameters and return
generateCpp(std::ostream & ostr,const SourceFileAttributes & attributes,bool includePrototype,bool cppInterface,const std::string & contextId)2762     void generateCpp(std::ostream& ostr,
2763                      const SourceFileAttributes& attributes,
2764                      bool includePrototype,
2765                      bool cppInterface,
2766                      const std::string& contextId) {
2767 
2768         // process each attribute
2769         for(std::vector<Attribute>::const_iterator
2770             it = attributes.begin(); it != attributes.end(); ++it) {
2771 
2772             // alias the attribute and function (bail if not export)
2773             const Attribute& attribute = *it;
2774             if (!attribute.isExportedFunction())
2775                 continue;
2776             const Function& function = attribute.function();
2777 
2778             // include prototype if requested
2779             if (includePrototype) {
2780                 ostr << "// " << function.name() << std::endl;
2781                 printFunction(ostr, function, false);
2782                 ostr << ";";
2783             }
2784 
2785             // write the C++ callable SEXP-based function (this version
2786             // returns errors via "try-error")
2787             ostr << std::endl;
2788             ostr << (cppInterface ? "static" : "RcppExport");
2789             ostr << " SEXP ";
2790             std::string funcName = contextId + "_" + function.name();
2791             ostr << funcName;
2792             if (cppInterface)
2793                 ostr << kTrySuffix;				// #nocov
2794             ostr << "(";
2795             std::ostringstream ostrArgs;
2796             const std::vector<Argument>& arguments = function.arguments();
2797             for (size_t i = 0; i<arguments.size(); i++) {
2798                 const Argument& argument = arguments[i];
2799                 ostrArgs << "SEXP " << argument.name() << "SEXP";
2800                 if (i != (arguments.size()-1))
2801                     ostrArgs << ", ";
2802             }
2803             std::string args = ostrArgs.str();
2804             ostr << args << ") {" << std::endl;
2805             ostr << "BEGIN_RCPP" << std::endl;
2806             if (!function.type().isVoid())
2807                 ostr << "    Rcpp::RObject rcpp_result_gen;" << std::endl;
2808             if (!cppInterface && attribute.rng())
2809                 ostr << "    Rcpp::RNGScope rcpp_rngScope_gen;" << std::endl;
2810             for (size_t i = 0; i<arguments.size(); i++) {
2811                 const Argument& argument = arguments[i];
2812 
2813                 ostr << "    Rcpp::traits::input_parameter< "
2814                      << argument.type().full_name() << " >::type " << argument.name()
2815                      << "(" << argument.name() << "SEXP);" << std::endl;
2816             }
2817 
2818             ostr << "    ";
2819             if (!function.type().isVoid())
2820                 ostr << "rcpp_result_gen = Rcpp::wrap(";
2821             ostr << function.name() << "(";
2822             for (size_t i = 0; i<arguments.size(); i++) {
2823                 const Argument& argument = arguments[i];
2824                 ostr << argument.name();
2825                 if (i != (arguments.size()-1))
2826                     ostr << ", ";
2827             }
2828             if (!function.type().isVoid())
2829                 ostr << ")";
2830             ostr << ");" << std::endl;
2831 
2832             if (!function.type().isVoid())
2833             {
2834                 ostr << "    return rcpp_result_gen;" << std::endl;
2835             }
2836             else
2837             {
2838                 ostr << "    return R_NilValue;" << std::endl;
2839             }
2840             ostr << (cppInterface ? "END_RCPP_RETURN_ERROR" : "END_RCPP")
2841                  << std::endl;
2842             ostr << "}" << std::endl;
2843 
2844             // Now write an R wrapper that returns error via Rf_error
2845             if (cppInterface) {
2846                 ostr << "RcppExport SEXP " << funcName << "(" << args << ") {"	// #nocov start
2847                      << std::endl;
2848                 ostr << "    SEXP rcpp_result_gen;" << std::endl;
2849                 ostr << "    {" << std::endl;
2850                 if (attribute.rng())
2851                     ostr << "        Rcpp::RNGScope rcpp_rngScope_gen;" << std::endl;
2852                 ostr << "        rcpp_result_gen = PROTECT(" << funcName
2853                      << kTrySuffix << "(";
2854                 for (size_t i = 0; i<arguments.size(); i++) {
2855                     const Argument& argument = arguments[i];
2856                     ostr << argument.name() << "SEXP";
2857                     if (i != (arguments.size()-1))
2858                         ostr << ", ";
2859                 }
2860                 ostr << "));" << std::endl;
2861                 ostr << "    }" << std::endl;
2862                 ostr << "    Rboolean rcpp_isInterrupt_gen = Rf_inherits(rcpp_result_gen, \"interrupted-error\");"
2863                      << std::endl
2864                      << "    if (rcpp_isInterrupt_gen) {" << std::endl
2865                      << "        UNPROTECT(1);" << std::endl
2866                      << "        Rf_onintr();" << std::endl
2867                      << "    }" << std::endl
2868                      << "    bool rcpp_isLongjump_gen = Rcpp::internal::isLongjumpSentinel(rcpp_result_gen);" << std::endl
2869                      << "    if (rcpp_isLongjump_gen) {" << std::endl
2870                                  // No need to unprotect before jump
2871                      << "        Rcpp::internal::resumeJump(rcpp_result_gen);" << std::endl
2872                      << "    }" << std::endl
2873                      << "    Rboolean rcpp_isError_gen = Rf_inherits(rcpp_result_gen, \"try-error\");"
2874                      << std::endl
2875                      << "    if (rcpp_isError_gen) {" << std::endl
2876                      << "        SEXP rcpp_msgSEXP_gen = Rf_asChar(rcpp_result_gen);" << std::endl
2877                      << "        UNPROTECT(1);" << std::endl
2878                      << "        Rf_error(CHAR(rcpp_msgSEXP_gen));" << std::endl
2879                      << "    }" << std::endl
2880                      << "    UNPROTECT(1);" << std::endl
2881                      << "    return rcpp_result_gen;" << std::endl
2882                      << "}" << std::endl;				// #nocov end
2883             }
2884         }
2885     }
2886 
2887 } // namespace attributes
2888 } // namespace Rcpp
2889 
2890 
2891 
2892 
2893 
2894 // provide implementations for util
2895 namespace Rcpp {
2896 namespace attributes {
2897 
2898     // Utility class for getting file existence and last modified time
FileInfo(const std::string & path)2899     FileInfo::FileInfo(const std::string& path)
2900         : path_(path), exists_(false), lastModified_(0)
2901     {
2902     #ifdef _WIN32
2903         struct _stat buffer;
2904         int result = _stat(path.c_str(), &buffer);
2905     #else
2906         struct stat buffer;
2907         int result = stat(path.c_str(), &buffer);
2908     #endif
2909         if (result != 0) {
2910             if (errno == ENOENT)
2911                 exists_ = false;
2912             else
2913                 throw Rcpp::file_io_error(errno, path);				// #nocov
2914         } else {
2915             exists_ = true;
2916             lastModified_ = static_cast<double>(buffer.st_mtime);
2917         }
2918     }
2919 
2920     // Remove a file (call back into R for this)
removeFile(const std::string & path)2921     bool removeFile(const std::string& path) {
2922         if (FileInfo(path).exists()) {
2923             Rcpp::Function rm = Rcpp::Environment::base_env()["file.remove"];	// #nocov start
2924             rm(path);
2925             return true;							// #nocov end
2926         }
2927         else {
2928             return false;
2929         }
2930     }
2931 
2932     // Recursively create a directory (call back into R for this)
createDirectory(const std::string & path)2933     void createDirectory(const std::string& path) {				// #nocov start
2934         if (!FileInfo(path).exists()) {
2935             Rcpp::Function mkdir = Rcpp::Environment::base_env()["dir.create"];
2936             mkdir(path, Rcpp::Named("recursive") = true);
2937         }
2938     }										// #nocov end
2939 
2940      // Known whitespace chars
2941     const char * const kWhitespaceChars = " \f\n\r\t\v";
2942 
2943     // Query whether a character is whitespace
isWhitespace(char ch)2944     bool isWhitespace(char ch) {
2945         return std::strchr(kWhitespaceChars, ch) != NULL;
2946     }
2947 
2948     // Remove trailing line comments -- ie, find comments that don't begin
2949     // a line, and remove them. We avoid stripping attributes.
stripTrailingLineComments(std::string * pStr)2950     void stripTrailingLineComments(std::string* pStr) {
2951 
2952         if (pStr->empty()) return;
2953 
2954         size_t len = pStr->length();
2955         bool inString = false;
2956         size_t idx = 0;
2957 
2958         // if this is an roxygen comment, then bail
2959         if (isRoxygenCpp(*pStr)) return;
2960 
2961         // skip over initial whitespace
2962         idx = pStr->find_first_not_of(kWhitespaceChars);
2963         if (idx == std::string::npos) return;
2964 
2965         // skip over a first comment
2966         if (idx + 1 < len && pStr->at(idx) == '/' && pStr->at(idx + 1) == '/') {
2967             idx = idx + 2;
2968         }
2969 
2970         // since we are searching for "//", we iterate up to 2nd last character
2971         while (idx < len - 1) {
2972 
2973             if (inString) {
2974                 if (pStr->at(idx) == '"' && pStr->at(idx - 1) != '\\') {
2975                     inString = false;
2976                 }
2977             } else {
2978                 if (pStr->at(idx) == '"') {
2979                     inString = true;
2980                 }
2981             }
2982 
2983             if (!inString &&
2984                 pStr->at(idx) == '/' &&
2985                 pStr->at(idx + 1) == '/') {
2986                 pStr->erase(idx);
2987                 return;
2988             }
2989             ++idx;
2990         }
2991     }
2992 
2993     // Trim a string
trimWhitespace(std::string * pStr)2994     void trimWhitespace(std::string* pStr) {
2995 
2996         // skip empty case
2997         if (pStr->empty())
2998             return;							// #nocov
2999 
3000         // trim right
3001         std::string::size_type pos = pStr->find_last_not_of(kWhitespaceChars);
3002         if (pos != std::string::npos)
3003             pStr->erase(pos + 1);
3004 
3005         // trim left
3006         pos = pStr->find_first_not_of(kWhitespaceChars);
3007         pStr->erase(0, pos);
3008     }
3009 
3010     // Strip balanced quotes from around a string (assumes already trimmed)
stripQuotes(std::string * pStr)3011     void stripQuotes(std::string* pStr) {
3012         if (pStr->length() < 2)
3013             return;
3014         char quote = *(pStr->begin());
3015         if ( (quote == '\'' || quote == '\"') && (*(pStr->rbegin()) == quote) )
3016             *pStr = pStr->substr(1, pStr->length()-2);			// #nocov
3017     }
3018 
3019     // is the passed string quoted?
isQuoted(const std::string & str)3020     bool isQuoted(const std::string& str) {
3021         if (str.length() < 2)
3022             return false;						// #nocov
3023         char quote = *(str.begin());
3024         return (quote == '\'' || quote == '\"') && (*(str.rbegin()) == quote);
3025     }
3026 
3027     // does a string end with another string?
endsWith(const std::string & str,const std::string & suffix)3028     bool endsWith(const std::string& str, const std::string& suffix)
3029     {
3030         return str.size() >= suffix.size() &&
3031                str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
3032     }
3033 
3034     // show a warning message
showWarning(const std::string & msg)3035     void showWarning(const std::string& msg) {				// #nocov start
3036         Rcpp::Function warning = Rcpp::Environment::base_env()["warning"];
3037         warning(msg, Rcpp::Named("call.") = false);
3038     }									// #nocov end
3039 
isRoxygenCpp(const std::string & str)3040     bool isRoxygenCpp(const std::string& str) {
3041         size_t len = str.length();
3042         if (len < 3) return false;
3043         size_t idx = str.find_first_not_of(kWhitespaceChars);
3044         if (idx == std::string::npos) return false;
3045 
3046         // make sure there are characters to check
3047         if (len - 2 < idx) return false;
3048 
3049         if (str[idx] == '/' &&
3050             str[idx + 1] == '/' &&
3051             str[idx + 2] == '\'') {
3052             return true;
3053         }
3054 
3055         return false;
3056 
3057     }
3058 
3059 } // namespace attributes
3060 } // namespace Rcpp
3061 
3062 
3063 /*******************************************************************
3064  * Attributes.cpp
3065  *******************************************************************/
3066 
3067 using namespace Rcpp::attributes;
3068 
3069 // Implementation helpers for sourceCppContext
3070 namespace {
3071 
3072     // Class that manages generation of source code for the sourceCpp dynlib
3073     class SourceCppDynlib {
3074     public:
SourceCppDynlib()3075         SourceCppDynlib() {}
3076 
SourceCppDynlib(const std::string & cacheDir,const std::string & cppSourcePath,Rcpp::List platform)3077         SourceCppDynlib(const std::string& cacheDir,
3078                         const std::string& cppSourcePath,
3079                         Rcpp::List platform)
3080             :  cppSourcePath_(cppSourcePath)
3081 
3082         {
3083             // get cpp source file info
3084             FileInfo cppSourceFilenameInfo(cppSourcePath_);
3085             if (!cppSourceFilenameInfo.exists())
3086                 throw Rcpp::file_not_found(cppSourcePath_);		// #nocov
3087 
3088             // record the base name of the source file
3089             Rcpp::Function basename = Rcpp::Environment::base_env()["basename"];
3090             cppSourceFilename_ = Rcpp::as<std::string>(basename(cppSourcePath_));
3091 
3092             // get platform info
3093             fileSep_ = Rcpp::as<std::string>(platform["file.sep"]);
3094             dynlibExt_ = Rcpp::as<std::string>(platform["dynlib.ext"]);
3095 
3096             // generate temp directory
3097             Rcpp::Function tempfile = Rcpp::Environment::base_env()["tempfile"];
3098             buildDirectory_ = Rcpp::as<std::string>(tempfile("sourcecpp_", cacheDir));
3099             std::replace(buildDirectory_.begin(), buildDirectory_.end(), '\\', '/');
3100             Rcpp::Function dircreate = Rcpp::Environment::base_env()["dir.create"];
3101             dircreate(buildDirectory_);
3102 
3103             // generate a random context id
3104             contextId_ = "sourceCpp_" + uniqueToken(cacheDir);
3105 
3106             // regenerate the source code
3107             regenerateSource(cacheDir);
3108         }
3109 
3110         // create from list
SourceCppDynlib(const Rcpp::List & dynlib)3111         explicit SourceCppDynlib(const Rcpp::List& dynlib)
3112         {
3113             using namespace Rcpp;
3114 
3115             cppSourcePath_ = as<std::string>(dynlib["cppSourcePath"]);
3116             generatedCpp_ = as<std::string>(dynlib["generatedCpp"]);
3117             cppSourceFilename_ = as<std::string>(dynlib["cppSourceFilename"]);
3118             contextId_ = as<std::string>(dynlib["contextId"]);
3119             buildDirectory_ = as<std::string>(dynlib["buildDirectory"]);
3120             fileSep_ = as<std::string>(dynlib["fileSep"]);
3121             dynlibFilename_ = as<std::string>(dynlib["dynlibFilename"]);
3122             previousDynlibFilename_ = as<std::string>(dynlib["previousDynlibFilename"]);
3123             dynlibExt_ = as<std::string>(dynlib["dynlibExt"]);
3124             exportedFunctions_ = as<std::vector<std::string> >(dynlib["exportedFunctions"]);
3125             modules_ = as<std::vector<std::string> >(dynlib["modules"]);
3126             depends_ = as<std::vector<std::string> >(dynlib["depends"]);
3127             plugins_ = as<std::vector<std::string> >(dynlib["plugins"]);
3128             embeddedR_ = as<std::vector<std::string> >(dynlib["embeddedR"]);
3129             List sourceDependencies = as<List>(dynlib["sourceDependencies"]);
3130             for (R_xlen_t i = 0; i<sourceDependencies.length(); i++) {
3131                 List fileInfo = as<List>(sourceDependencies.at(i));	// #nocov
3132                 sourceDependencies_.push_back(FileInfo(fileInfo)); 	// #nocov
3133             }
3134         }
3135 
3136         // convert to list
toList() const3137         Rcpp::List toList() const {
3138             using namespace Rcpp;
3139             List dynlib;
3140             dynlib["cppSourcePath"] = cppSourcePath_;
3141             dynlib["generatedCpp"] = generatedCpp_;
3142             dynlib["cppSourceFilename"] = cppSourceFilename_;
3143             dynlib["contextId"] = contextId_;
3144             dynlib["buildDirectory"] = buildDirectory_;
3145             dynlib["fileSep"] = fileSep_;
3146             dynlib["dynlibFilename"] = dynlibFilename_;
3147             dynlib["previousDynlibFilename"] = previousDynlibFilename_;
3148             dynlib["dynlibExt"] = dynlibExt_;
3149             dynlib["exportedFunctions"] = exportedFunctions_;
3150             dynlib["modules"] = modules_;
3151             dynlib["depends"] = depends_;
3152             dynlib["plugins"] = plugins_;
3153             dynlib["embeddedR"] = embeddedR_;
3154             List sourceDependencies;
3155             for (std::size_t i = 0; i<sourceDependencies_.size(); i++) {
3156                 FileInfo fileInfo = sourceDependencies_.at(i);
3157                 sourceDependencies.push_back(fileInfo.toList());
3158             }
3159             dynlib["sourceDependencies"] = sourceDependencies;
3160 
3161             return dynlib;
3162         }
3163 
isEmpty() const3164         bool isEmpty() const { return cppSourcePath_.empty(); }
3165 
isBuilt() const3166         bool isBuilt() const { return FileInfo(dynlibPath()).exists(); };
3167 
isSourceDirty() const3168         bool isSourceDirty() const {
3169             // source file out of date means we're dirty
3170             if (FileInfo(cppSourcePath_).lastModified() >
3171                 FileInfo(generatedCppSourcePath()).lastModified())
3172                 return true;				// #nocov
3173 
3174             // no dynlib means we're dirty
3175             if (!FileInfo(dynlibPath()).exists())
3176                 return true;				// #nocov
3177 
3178             // variation in source dependencies means we're dirty
3179             std::vector<FileInfo> sourceDependencies = parseSourceDependencies(
3180                                                             cppSourcePath_);
3181             if (sourceDependencies != sourceDependencies_)
3182                 return true;				// #nocov
3183 
3184             // not dirty
3185             return false;
3186         }
3187 
regenerateSource(const std::string & cacheDir)3188         void regenerateSource(const std::string& cacheDir) {
3189 
3190             // create new dynlib filename
3191             previousDynlibFilename_ = dynlibFilename_;
3192             dynlibFilename_ = "sourceCpp_" + uniqueToken(cacheDir) + dynlibExt_;
3193 
3194             // copy the source file to the build dir
3195             Rcpp::Function filecopy = Rcpp::Environment::base_env()["file.copy"];
3196             filecopy(cppSourcePath_, generatedCppSourcePath(), true);
3197 
3198             // parse attributes
3199             SourceFileAttributesParser sourceAttributes(cppSourcePath_, "", true);
3200 
3201             // generate cpp for attributes and append them
3202             std::ostringstream ostr;
3203             // always include Rcpp.h in case the user didn't
3204             ostr << std::endl << std::endl;
3205             ostr << "#include <Rcpp.h>" << std::endl;
3206             // initialize references to global Rostreams
3207             initializeGlobals(ostr);
3208             generateCpp(ostr, sourceAttributes, true, false, contextId_);
3209             generatedCpp_ = ostr.str();
3210             std::ofstream cppOfs(generatedCppSourcePath().c_str(),
3211                                  std::ofstream::out | std::ofstream::app);
3212             if (cppOfs.fail())
3213                 throw Rcpp::file_io_error(generatedCppSourcePath());	// #nocov
3214             cppOfs << generatedCpp_;
3215             cppOfs.close();
3216 
3217             // generate R for attributes and write it into the build directory
3218             std::ofstream rOfs(generatedRSourcePath().c_str(),
3219                                std::ofstream::out | std::ofstream::trunc);
3220             if (rOfs.fail())
3221                 throw Rcpp::file_io_error(generatedRSourcePath());	// #nocov
3222 
3223             // DLLInfo - hide using . and ensure uniqueness using contextId
3224             std::string dllInfo = "`." + contextId_ + "_DLLInfo`";
3225             rOfs << dllInfo << " <- dyn.load('" << dynlibPath() << "')"
3226                  << std::endl << std::endl;
3227 
3228             // Generate R functions
3229             generateR(rOfs, sourceAttributes, dllInfo);
3230 
3231             // remove the DLLInfo
3232             rOfs << std::endl << "rm(" << dllInfo << ")"
3233                  << std::endl;
3234 
3235             rOfs.close();
3236 
3237             // discover exported functions and dependencies
3238             exportedFunctions_.clear();
3239             depends_.clear();
3240             plugins_.clear();
3241             for (SourceFileAttributesParser::const_iterator
3242               it = sourceAttributes.begin(); it != sourceAttributes.end(); ++it) {
3243 
3244                  if (it->name() == kExportAttribute && !it->function().empty())
3245                     exportedFunctions_.push_back(it->exportedName());
3246 
3247                  else if (it->name() == kDependsAttribute) {
3248                      for (size_t i = 0; i<it->params().size(); ++i)	// #nocov
3249                         depends_.push_back(it->params()[i].name());	// #nocov
3250                  }
3251 
3252                  else if (it->name() == kPluginsAttribute) {
3253                      for (size_t i = 0; i<it->params().size(); ++i)
3254                         plugins_.push_back(it->params()[i].name());
3255                  }
3256             }
3257 
3258             // capture modules
3259             modules_ = sourceAttributes.modules();
3260 
3261             // capture embededded R
3262             embeddedR_ = sourceAttributes.embeddedR();
3263 
3264             // capture source dependencies
3265             sourceDependencies_ = sourceAttributes.sourceDependencies();
3266         }
3267 
contextId() const3268         const std::string& contextId() const {
3269             return contextId_;
3270         }
3271 
cppSourcePath() const3272         const std::string& cppSourcePath() const {
3273             return cppSourcePath_;
3274         }
3275 
cppDependencySourcePaths()3276         const std::vector<std::string> cppDependencySourcePaths() {
3277             std::vector<std::string> dependencies;
3278             for (size_t i = 0; i<sourceDependencies_.size(); ++i) {
3279                 FileInfo dep = sourceDependencies_[i];
3280                 if (dep.extension() == ".cc" || dep.extension() == ".cpp") {
3281                     dependencies.push_back(dep.path());			// #nocov
3282                 }
3283             }
3284             return dependencies;
3285         }
3286 
buildDirectory() const3287         std::string buildDirectory() const {
3288             return buildDirectory_;
3289         }
3290 
generatedCpp() const3291         std::string generatedCpp() const {
3292             return generatedCpp_;
3293         }
3294 
cppSourceFilename() const3295         std::string cppSourceFilename() const {
3296             return cppSourceFilename_;
3297         }
3298 
rSourceFilename() const3299         std::string rSourceFilename() const {
3300             return cppSourceFilename() + ".R";
3301         }
3302 
dynlibFilename() const3303         std::string dynlibFilename() const {
3304             return dynlibFilename_;
3305         }
3306 
dynlibPath() const3307         std::string dynlibPath() const {
3308             return buildDirectory_ + fileSep_ + dynlibFilename();
3309         }
3310 
previousDynlibPath() const3311         std::string previousDynlibPath() const {
3312             if (!previousDynlibFilename_.empty())
3313                 return buildDirectory_ + fileSep_ + previousDynlibFilename_;	// #nocov
3314             else
3315                 return std::string();
3316         }
3317 
exportedFunctions() const3318         const std::vector<std::string>& exportedFunctions() const {
3319             return exportedFunctions_;
3320         }
3321 
modules() const3322         const std::vector<std::string>& modules() const {
3323             return modules_;
3324         }
3325 
depends() const3326         const std::vector<std::string>& depends() const { return depends_; };
3327 
plugins() const3328         const std::vector<std::string>& plugins() const { return plugins_; };
3329 
embeddedR() const3330         const std::vector<std::string>& embeddedR() const { return embeddedR_; }
3331 
3332     private:
3333 
generatedCppSourcePath() const3334         std::string generatedCppSourcePath() const {
3335            return buildDirectory_ + fileSep_ + cppSourceFilename();
3336         }
3337 
generatedRSourcePath() const3338          std::string generatedRSourcePath() const {
3339            return buildDirectory_ + fileSep_ + rSourceFilename();
3340         }
3341 
generateR(std::ostream & ostr,const SourceFileAttributes & attributes,const std::string & dllInfo) const3342         void generateR(std::ostream& ostr,
3343                        const SourceFileAttributes& attributes,
3344                        const std::string& dllInfo) const
3345         {
3346             // process each attribute
3347             for(std::vector<Attribute>::const_iterator
3348                 it = attributes.begin(); it != attributes.end(); ++it) {
3349 
3350                 // alias the attribute and function (bail if not export)
3351                 const Attribute& attribute = *it;
3352                 if (!attribute.isExportedFunction())
3353                     continue;
3354                 const Function& function = attribute.function();
3355 
3356                 // export the function
3357                 ostr <<  attribute.exportedName()
3358                      << " <- Rcpp:::sourceCppFunction("
3359                      << "function(" << generateRArgList(function) << ") {}, "
3360                      << (function.type().isVoid() ? "TRUE" : "FALSE") << ", "
3361                      << dllInfo << ", "
3362                      << "'" << contextId_ + "_" + function.name()
3363                      << "')" << std::endl;
3364             }
3365 
3366             // modules
3367             std::vector<std::string> modules = attributes.modules();
3368             if (modules.size() > 0)
3369             {
3370                 // modules require definition of C++Object to be loaded
3371                 ostr << "library(Rcpp)" << std::endl;
3372 
3373                 // load each module
3374                 for (std::vector<std::string>::const_iterator
3375                     it = modules.begin(); it != modules.end(); ++it)
3376                 {
3377                     ostr << " populate( Rcpp::Module(\"" << *it << "\","
3378                          << dllInfo << "), environment() ) " << std::endl;
3379                 }
3380             }
3381 
3382         }
3383 
uniqueToken(const std::string & cacheDir)3384         std::string uniqueToken(const std::string& cacheDir) {
3385             Rcpp::Environment rcppEnv = Rcpp::Environment::namespace_env("Rcpp");
3386             Rcpp::Function uniqueTokenFunc = rcppEnv[".sourceCppDynlibUniqueToken"];
3387             return Rcpp::as<std::string>(uniqueTokenFunc(cacheDir));
3388         }
3389 
3390     private:
3391         std::string cppSourcePath_;
3392         std::string generatedCpp_;
3393         std::string cppSourceFilename_;
3394         std::string contextId_;
3395         std::string buildDirectory_;
3396         std::string fileSep_;
3397         std::string dynlibFilename_;
3398         std::string previousDynlibFilename_;
3399         std::string dynlibExt_;
3400         std::vector<std::string> exportedFunctions_;
3401         std::vector<std::string> modules_;
3402         std::vector<std::string> depends_;
3403         std::vector<std::string> plugins_;
3404         std::vector<std::string> embeddedR_;
3405         std::vector<FileInfo> sourceDependencies_;
3406     };
3407 
3408     // Dynlib cache that allows lookup by either file path or code contents
3409 
dynlibCacheInsert(const std::string & cacheDir,const std::string & file,const std::string & code,const SourceCppDynlib & dynlib)3410     void dynlibCacheInsert(const std::string& cacheDir,
3411                            const std::string& file,
3412                            const std::string& code,
3413                            const SourceCppDynlib& dynlib)
3414     {
3415         Rcpp::Environment rcppEnv = Rcpp::Environment::namespace_env("Rcpp");
3416         Rcpp::Function dynlibInsertFunc = rcppEnv[".sourceCppDynlibInsert"];
3417         dynlibInsertFunc(cacheDir, file, code, dynlib.toList());
3418     }
3419 
dynlibCacheInsertFile(const std::string & cacheDir,const std::string & file,const SourceCppDynlib & dynlib)3420     void dynlibCacheInsertFile(const std::string& cacheDir,
3421                                const std::string& file,
3422                                const SourceCppDynlib& dynlib)
3423     {
3424         dynlibCacheInsert(cacheDir, file, "", dynlib);
3425     }
3426 
dynlibCacheInsertCode(const std::string & cacheDir,const std::string & code,const SourceCppDynlib & dynlib)3427     void dynlibCacheInsertCode(const std::string& cacheDir,
3428                                const std::string& code,
3429                                const SourceCppDynlib& dynlib)
3430     {
3431         dynlibCacheInsert(cacheDir, "", code, dynlib);
3432     }
3433 
dynlibCacheLookup(const std::string & cacheDir,const std::string & file,const std::string & code)3434     SourceCppDynlib dynlibCacheLookup(const std::string& cacheDir,
3435                                       const std::string& file,
3436                                       const std::string& code)
3437     {
3438         Rcpp::Environment rcppEnv = Rcpp::Environment::namespace_env("Rcpp");
3439         Rcpp::Function dynlibLookupFunc = rcppEnv[".sourceCppDynlibLookup"];
3440         Rcpp::List dynlibList = dynlibLookupFunc(cacheDir, file, code);
3441         if (dynlibList.length() > 0)
3442             return SourceCppDynlib(dynlibList);
3443         else
3444             return SourceCppDynlib();
3445     }
3446 
dynlibCacheLookupByFile(const std::string & cacheDir,const std::string & file)3447     SourceCppDynlib dynlibCacheLookupByFile(const std::string& cacheDir,
3448                                             const std::string& file)
3449     {
3450         return dynlibCacheLookup(cacheDir, file, "");
3451     }
3452 
dynlibCacheLookupByCode(const std::string & cacheDir,const std::string & code)3453     SourceCppDynlib dynlibCacheLookupByCode(const std::string& cacheDir,
3454                                             const std::string& code)
3455     {
3456         return dynlibCacheLookup(cacheDir, "", code);
3457     }
3458 
3459 } // anonymous namespace
3460 
3461 // Create temporary build directory, generate code as necessary, and return
3462 // the context required for the sourceCpp function to complete it's work
sourceCppContext(SEXP sFile,SEXP sCode,SEXP sRebuild,SEXP sCacheDir,SEXP sPlatform)3463 RcppExport SEXP sourceCppContext(SEXP sFile, SEXP sCode,
3464                                  SEXP sRebuild, SEXP sCacheDir, SEXP sPlatform) {
3465 BEGIN_RCPP
3466     // parameters
3467     std::string file = Rcpp::as<std::string>(sFile);
3468     std::string code = sCode != R_NilValue ? Rcpp::as<std::string>(sCode) : "";
3469     bool rebuild = Rcpp::as<bool>(sRebuild);
3470     std::string cacheDir = Rcpp::as<std::string>(sCacheDir);
3471     Rcpp::List platform = Rcpp::as<Rcpp::List>(sPlatform);
3472 
3473     // get dynlib (using cache if possible)
3474     SourceCppDynlib dynlib = !code.empty() ? dynlibCacheLookupByCode(cacheDir, code)
3475                                            : dynlibCacheLookupByFile(cacheDir, file);
3476 
3477     // check dynlib build state
3478     bool buildRequired = false;
3479 
3480     // if there is no dynlib in the cache then create a new one
3481     if (dynlib.isEmpty()) {
3482         buildRequired = true;
3483         dynlib = SourceCppDynlib(cacheDir, file, platform);
3484     }
3485 
3486     // if the cached dynlib is dirty then regenerate the source
3487     else if (rebuild || dynlib.isSourceDirty()) {
3488         buildRequired = true;				// #nocov
3489         dynlib.regenerateSource(cacheDir);		// #nocov
3490     }
3491 
3492     // if the dynlib hasn't yet been built then note that
3493     else if (!dynlib.isBuilt()) {
3494         buildRequired = true;				// #nocov
3495     }
3496 
3497     // save the dynlib to the cache
3498     if (!code.empty())
3499         dynlibCacheInsertCode(cacheDir, code, dynlib);
3500     else
3501         dynlibCacheInsertFile(cacheDir, file, dynlib);
3502 
3503     // return context as a list
3504     using namespace Rcpp;
3505     return List::create(
3506         _["contextId"] = dynlib.contextId(),
3507         _["cppSourcePath"] = dynlib.cppSourcePath(),
3508         _["cppDependencySourcePaths"] = dynlib.cppDependencySourcePaths(),
3509         _["buildRequired"] = buildRequired,
3510         _["buildDirectory"] = dynlib.buildDirectory(),
3511         _["generatedCpp"] = dynlib.generatedCpp(),
3512         _["exportedFunctions"] = dynlib.exportedFunctions(),
3513         _["modules"] = dynlib.modules(),
3514         _["cppSourceFilename"] = dynlib.cppSourceFilename(),
3515         _["rSourceFilename"] = dynlib.rSourceFilename(),
3516         _["dynlibFilename"] = dynlib.dynlibFilename(),
3517         _["dynlibPath"] = dynlib.dynlibPath(),
3518         _["previousDynlibPath"] = dynlib.previousDynlibPath(),
3519         _["depends"] = dynlib.depends(),
3520         _["plugins"] = dynlib.plugins(),
3521         _["embeddedR"] = dynlib.embeddedR());
3522 END_RCPP
3523 }
3524 
3525 // Compile the attributes within the specified package directory into
3526 // RcppExports.cpp and RcppExports.R
compileAttributes(SEXP sPackageDir,SEXP sPackageName,SEXP sDepends,SEXP sRegistration,SEXP sCppFiles,SEXP sCppFileBasenames,SEXP sIncludes,SEXP sVerbose,SEXP sPlatform)3527 RcppExport SEXP compileAttributes(SEXP sPackageDir,
3528                                   SEXP sPackageName,
3529                                   SEXP sDepends,
3530                                   SEXP sRegistration,
3531                                   SEXP sCppFiles,
3532                                   SEXP sCppFileBasenames,
3533                                   SEXP sIncludes,
3534                                   SEXP sVerbose,
3535                                   SEXP sPlatform) {
3536 BEGIN_RCPP
3537     // arguments
3538     std::string packageDir = Rcpp::as<std::string>(sPackageDir);
3539     std::string packageName = Rcpp::as<std::string>(sPackageName);
3540 
3541     Rcpp::CharacterVector vDepends = Rcpp::as<Rcpp::CharacterVector>(sDepends);
3542     std::set<std::string> depends;
3543     for (Rcpp::CharacterVector::iterator
3544                         it = vDepends.begin(); it != vDepends.end(); ++it) {
3545         depends.insert(std::string(*it));
3546     }
3547 
3548     bool registration = Rcpp::as<bool>(sRegistration);
3549 
3550     std::vector<std::string> cppFiles =
3551                     Rcpp::as<std::vector<std::string> >(sCppFiles);
3552     std::vector<std::string> cppFileBasenames =
3553                     Rcpp::as<std::vector<std::string> >(sCppFileBasenames);
3554     std::vector<std::string> includes =
3555                     Rcpp::as<std::vector<std::string> >(sIncludes);
3556     bool verbose = Rcpp::as<bool>(sVerbose);
3557     Rcpp::List platform = Rcpp::as<Rcpp::List>(sPlatform);
3558     std::string fileSep = Rcpp::as<std::string>(platform["file.sep"]);
3559 
3560     // initialize generators
3561     ExportsGenerators generators;
3562     generators.add(new CppExportsGenerator(packageDir, packageName, fileSep));
3563     generators.add(new RExportsGenerator(packageDir, packageName, registration, fileSep));
3564 
3565     // catch file exists exception if the include file already exists
3566     // and we are unable to overwrite it
3567     try {
3568         generators.add(new CppExportsIncludeGenerator(packageDir,
3569                                                       packageName,
3570                                                       fileSep));
3571     }
3572     catch(const Rcpp::file_exists& e) {
3573         std::string msg =
3574             "The header file '" + e.filePath() + "' already exists so "
3575             "cannot be overwritten by Rcpp::interfaces";
3576         throw Rcpp::exception(msg.c_str(), __FILE__, __LINE__);
3577     }
3578 
3579     // catch file exists exception for package include (because if it
3580     // already exists we simply leave it alone)
3581     try {
3582         generators.add(new CppPackageIncludeGenerator(packageDir,
3583                                                       packageName,
3584                                                       fileSep));
3585     }
3586     catch(const Rcpp::file_exists& e) {}
3587 
3588     // write begin
3589     generators.writeBegin();
3590 
3591     // Parse attributes from each file and generate code as required.
3592     bool hasPackageInit = false;
3593     bool haveAttributes = false;
3594     std::set<std::string> dependsAttribs;
3595     for (std::size_t i=0; i<cppFiles.size(); i++) {
3596 
3597         // don't process RcppExports.cpp
3598         std::string cppFile = cppFiles[i];
3599         if (endsWith(cppFile, "RcppExports.cpp"))
3600             continue;						// #nocov
3601 
3602         // parse file
3603         SourceFileAttributesParser attributes(cppFile, packageName, false);
3604 
3605         // note if we found a package init function
3606         if (!hasPackageInit && attributes.hasPackageInit())
3607             hasPackageInit = true;				// #nocov
3608 
3609         // continue if no generator output
3610         if (!attributes.hasGeneratorOutput())
3611             continue;						// #nocov
3612 
3613         // confirm we have attributes
3614         haveAttributes = true;
3615 
3616         // write functions
3617         generators.writeFunctions(attributes, verbose);
3618 
3619         // track depends
3620         for (SourceFileAttributesParser::const_iterator
3621                      it = attributes.begin(); it != attributes.end(); ++it) {
3622             if (it->name() == kDependsAttribute) {
3623                 for (size_t i = 0; i<it->params().size(); ++i)		// #nocov
3624                     dependsAttribs.insert(it->params()[i].name());	// #nocov
3625             }
3626         }
3627     }
3628 
3629     // write end
3630     generators.writeEnd(hasPackageInit);
3631 
3632     // commit or remove
3633     std::vector<std::string> updated;
3634     if (haveAttributes)
3635         updated = generators.commit(includes);
3636     else
3637         updated = generators.remove();					// #nocov
3638 
3639     // print warning if there are depends attributes that don't have
3640     // corresponding entries in the DESCRIPTION file
3641     std::vector<std::string> diff;
3642     std::set_difference(dependsAttribs.begin(), dependsAttribs.end(),
3643                         depends.begin(), depends.end(),
3644                         std::back_inserter(diff));
3645     if (!diff.empty()) {
3646         std::string msg =						// #nocov start
3647            "The following packages are referenced using Rcpp::depends "
3648            "attributes however are not listed in the Depends, Imports or "
3649            "LinkingTo fields of the package DESCRIPTION file: ";
3650         for (size_t i=0; i<diff.size(); i++) {
3651             msg += diff[i];
3652             if (i != (diff.size()-1))
3653                 msg += ", ";
3654         }
3655         showWarning(msg);
3656     }
3657 
3658     // verbose output
3659     if (verbose) {
3660         for (size_t i=0; i<updated.size(); i++)
3661             Rcpp::Rcout << updated[i] << " updated." << std::endl;	// #nocov end
3662     }
3663 
3664     // return files updated
3665     return Rcpp::wrap<std::vector<std::string> >(updated);
3666 END_RCPP
3667 }
3668