Validate FS (and RS) ASTs.
This change adds a new RSCheckAST class that is used to handle validation
of the final AST. The checks include existing RS checks (for accepted types)
and new checks for Filterscript (like disallowing pointers, unions and
> 32 bit types completely). The refactoring also fixes a subtle union bug
that existed in prior versions of llvm-rs-cc. We also add an implicit
pragma for "rs_fp_relaxed" by default on Filterscript.
Bug: 7166741
Change-Id: Ia077783b4caba0bbd89df2ab3827e9f4d1009eea
diff --git a/Android.mk b/Android.mk
index 748665a..da28351 100644
--- a/Android.mk
+++ b/Android.mk
@@ -231,6 +231,7 @@
llvm-rs-cc.cpp \
slang_rs.cpp \
slang_rs_ast_replace.cpp \
+ slang_rs_check_ast.cpp \
slang_rs_context.cpp \
slang_rs_pragma_handler.cpp \
slang_rs_backend.cpp \
diff --git a/slang_rs.cpp b/slang_rs.cpp
index af86e78..98b9771 100644
--- a/slang_rs.cpp
+++ b/slang_rs.cpp
@@ -40,6 +40,8 @@
namespace slang {
+#define FS_SUFFIX "fs"
+
#define RS_HEADER_SUFFIX "rsh"
/* RS_HEADER_ENTRY(name) */
@@ -61,6 +63,15 @@
RS_HEADER_ENTRY(rs_time) \
RS_HEADER_ENTRY(rs_types) \
+// Returns true if \p Filename ends in ".fs".
+bool SlangRS::isFilterscript(const char *Filename) {
+ const char *c = strrchr(Filename, '.');
+ if (c && !strncmp(FS_SUFFIX, c + 1, strlen(FS_SUFFIX) + 1)) {
+ return true;
+ } else {
+ return false;
+ }
+}
bool SlangRS::reflectToJava(const std::string &OutputPathBase,
const std::string &OutputPackageName,
@@ -233,7 +244,8 @@
OS,
OT,
getSourceManager(),
- mAllowRSPrefix);
+ mAllowRSPrefix,
+ mIsFilterscript);
}
bool SlangRS::IsRSHeaderFile(const char *File) {
@@ -245,9 +257,9 @@
return false;
}
-bool SlangRS::IsFunctionInRSHeaderFile(const clang::FunctionDecl *FD,
- const clang::SourceManager &SourceMgr) {
- clang::FullSourceLoc FSL(FD->getLocStart(), SourceMgr);
+bool SlangRS::IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
+ const clang::SourceManager &SourceMgr) {
+ clang::FullSourceLoc FSL(Loc, SourceMgr);
clang::PresumedLoc PLoc = SourceMgr.getPresumedLoc(FSL);
const char *Filename = PLoc.getFilename();
@@ -259,7 +271,8 @@
}
SlangRS::SlangRS()
- : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0) {
+ : Slang(), mRSContext(NULL), mAllowRSPrefix(false), mTargetAPI(0),
+ mIsFilterscript(false) {
}
bool SlangRS::compile(
@@ -329,6 +342,8 @@
JavaReflectionPackageName);
}
+ mIsFilterscript = isFilterscript(InputFile);
+
if (Slang::compile() > 0)
return false;
diff --git a/slang_rs.h b/slang_rs.h
index 571f1fa..8ad70de 100644
--- a/slang_rs.h
+++ b/slang_rs.h
@@ -29,10 +29,6 @@
#include "slang_rs_reflect_utils.h"
#include "slang_version.h"
-namespace clang {
- class FunctionDecl;
-}
-
namespace slang {
class RSContext;
class RSExportRecordType;
@@ -46,6 +42,8 @@
unsigned int mTargetAPI;
+ bool mIsFilterscript;
+
// Custom diagnostic identifiers
unsigned mDiagErrorInvalidOutputDepParameter;
unsigned mDiagErrorODR;
@@ -77,6 +75,9 @@
// and is valid before compile() ends.
bool checkODR(const char *CurInputFile);
+ // Returns true if this is a Filterscript file.
+ static bool isFilterscript(const char *Filename);
+
protected:
virtual void initDiagnostic();
virtual void initPreprocessor();
@@ -90,11 +91,11 @@
public:
static bool IsRSHeaderFile(const char *File);
- // FIXME: Determine whether a function is in RS header (i.e., one of the RS
+ // FIXME: Determine whether a location is in RS header (i.e., one of the RS
// built-in APIs) should only need its names (we need a "list" of RS
// built-in APIs).
- static bool IsFunctionInRSHeaderFile(const clang::FunctionDecl *FD,
- const clang::SourceManager &SourceMgr);
+ static bool IsLocInRSHeaderFile(const clang::SourceLocation &Loc,
+ const clang::SourceManager &SourceMgr);
SlangRS();
diff --git a/slang_rs_backend.cpp b/slang_rs_backend.cpp
index a1efad1..c643908 100644
--- a/slang_rs_backend.cpp
+++ b/slang_rs_backend.cpp
@@ -54,25 +54,29 @@
llvm::raw_ostream *OS,
Slang::OutputType OT,
clang::SourceManager &SourceMgr,
- bool AllowRSPrefix)
+ bool AllowRSPrefix,
+ bool IsFilterscript)
: Backend(DiagEngine, CodeGenOpts, TargetOpts, Pragmas, OS, OT),
mContext(Context),
mSourceMgr(SourceMgr),
mAllowRSPrefix(AllowRSPrefix),
+ mIsFilterscript(IsFilterscript),
mExportVarMetadata(NULL),
mExportFuncMetadata(NULL),
mExportForEachNameMetadata(NULL),
mExportForEachSignatureMetadata(NULL),
mExportTypeMetadata(NULL),
mRSObjectSlotsMetadata(NULL),
- mRefCount(mContext->getASTContext()) {
+ mRefCount(mContext->getASTContext()),
+ mASTChecker(mContext->getASTContext(), mContext->getTargetAPI(),
+ IsFilterscript) {
}
// 1) Add zero initialization of local RS object types
void RSBackend::AnnotateFunction(clang::FunctionDecl *FD) {
if (FD &&
FD->hasBody() &&
- !SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr)) {
+ !SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr)) {
mRefCount.Init();
mRefCount.Visit(FD->getBody());
}
@@ -90,7 +94,7 @@
continue;
if (!FD->getName().startswith("rs")) // Check prefix
continue;
- if (!SlangRS::IsFunctionInRSHeaderFile(FD, mSourceMgr))
+ if (!SlangRS::IsLocInRSHeaderFile(FD->getLocation(), mSourceMgr))
mDiagEngine.Report(
clang::FullSourceLoc(FD->getLocation(), mSourceMgr),
mDiagEngine.getCustomDiagID(clang::DiagnosticsEngine::Error,
@@ -125,53 +129,19 @@
return Backend::HandleTopLevelDecl(D);
}
-namespace {
-
-static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI) {
- if (!VD) {
- return true;
- }
-
- clang::ASTContext &C = VD->getASTContext();
- const clang::Type *T = VD->getType().getTypePtr();
- bool valid = true;
-
- if (VD->getLinkage() == clang::ExternalLinkage) {
- llvm::StringRef TypeName;
- if (!RSExportType::NormalizeType(T, TypeName, &C.getDiagnostics(), VD)) {
- valid = false;
- }
- }
- valid &= RSExportType::ValidateVarDecl(VD, TargetAPI);
-
- return valid;
-}
-
-static bool ValidateASTContext(clang::ASTContext &C, unsigned int TargetAPI) {
- bool valid = true;
- clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
- for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
- DE = TUDecl->decls_end();
- DI != DE;
- DI++) {
- clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI);
- if (VD && !ValidateVarDecl(VD, TargetAPI)) {
- valid = false;
- }
- }
-
- return valid;
-}
-
-} // namespace
void RSBackend::HandleTranslationUnitPre(clang::ASTContext &C) {
clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
- if (!ValidateASTContext(C, getTargetAPI())) {
+ // If we have an invalid RS/FS AST, don't check further.
+ if (!mASTChecker.Validate()) {
return;
}
+ if (mIsFilterscript) {
+ mContext->addPragma("rs_fp_relaxed", "");
+ }
+
int version = mContext->getVersion();
if (version == 0) {
// Not setting a version is an error
diff --git a/slang_rs_backend.h b/slang_rs_backend.h
index 0dc70e6..80c643d 100644
--- a/slang_rs_backend.h
+++ b/slang_rs_backend.h
@@ -19,6 +19,7 @@
#include "slang_backend.h"
#include "slang_pragma_recorder.h"
+#include "slang_rs_check_ast.h"
#include "slang_rs_object_ref_count.h"
namespace llvm {
@@ -47,6 +48,8 @@
bool mAllowRSPrefix;
+ bool mIsFilterscript;
+
llvm::NamedMDNode *mExportVarMetadata;
llvm::NamedMDNode *mExportFuncMetadata;
llvm::NamedMDNode *mExportForEachNameMetadata;
@@ -57,6 +60,8 @@
RSObjectRefCount mRefCount;
+ RSCheckAST mASTChecker;
+
void AnnotateFunction(clang::FunctionDecl *FD);
protected:
@@ -79,7 +84,8 @@
llvm::raw_ostream *OS,
Slang::OutputType OT,
clang::SourceManager &SourceMgr,
- bool AllowRSPrefix);
+ bool AllowRSPrefix,
+ bool IsFilterscript);
virtual ~RSBackend();
};
diff --git a/slang_rs_check_ast.cpp b/slang_rs_check_ast.cpp
new file mode 100644
index 0000000..118bda7
--- /dev/null
+++ b/slang_rs_check_ast.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "slang_rs_check_ast.h"
+
+#include "slang_assert.h"
+#include "slang_rs.h"
+#include "slang_rs_export_type.h"
+
+namespace slang {
+
+void RSCheckAST::VisitStmt(clang::Stmt *S) {
+ // This function does the actual iteration through all sub-Stmt's within
+ // a given Stmt. Note that this function is skipped by all of the other
+ // Visit* functions if we have already found a higher-level match.
+ for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end();
+ I != E;
+ I++) {
+ if (clang::Stmt *Child = *I) {
+ Visit(Child);
+ }
+ }
+}
+
+void RSCheckAST::ValidateFunctionDecl(clang::FunctionDecl *FD) {
+ if (!FD) {
+ return;
+ }
+
+ if (!mIsFilterscript) {
+ // No additional validation for non-Filterscript functions.
+ if (clang::Stmt *Body = FD->getBody()) {
+ Visit(Body);
+ }
+ return;
+ }
+
+ size_t numParams = FD->getNumParams();
+
+ clang::QualType resultType = FD->getResultType().getCanonicalType();
+
+ // We use FD as our NamedDecl in the case of a bad return type.
+ if (!RSExportType::ValidateType(C, resultType, FD,
+ FD->getLocStart(), mTargetAPI,
+ mIsFilterscript)) {
+ mValid = false;
+ }
+
+ for (size_t i = 0; i < numParams; i++) {
+ clang::ParmVarDecl *PVD = FD->getParamDecl(i);
+ clang::QualType QT = PVD->getType().getCanonicalType();
+ if (!RSExportType::ValidateType(C, QT, PVD, PVD->getLocStart(),
+ mTargetAPI, mIsFilterscript)) {
+ mValid = false;
+ }
+ }
+
+ if (clang::Stmt *Body = FD->getBody()) {
+ Visit(Body);
+ }
+}
+
+
+void RSCheckAST::ValidateVarDecl(clang::VarDecl *VD) {
+ if (!VD) {
+ return;
+ }
+
+ const clang::Type *T = VD->getType().getTypePtr();
+
+ if (VD->getLinkage() == clang::ExternalLinkage) {
+ llvm::StringRef TypeName;
+ if (!RSExportType::NormalizeType(T, TypeName, &mDiagEngine, VD)) {
+ mValid = false;
+ }
+ }
+
+ if (!RSExportType::ValidateVarDecl(VD, mTargetAPI, mIsFilterscript)) {
+ mValid = false;
+ } else if (clang::Expr *Init = VD->getInit()) {
+ // Only check the initializer if the decl is already ok.
+ Visit(Init);
+ }
+}
+
+
+void RSCheckAST::VisitDeclStmt(clang::DeclStmt *DS) {
+ if (!SlangRS::IsLocInRSHeaderFile(DS->getLocStart(), mSM)) {
+ for (clang::DeclStmt::decl_iterator I = DS->decl_begin(),
+ E = DS->decl_end();
+ I != E;
+ ++I) {
+ if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*I)) {
+ ValidateVarDecl(VD);
+ } else if (clang::FunctionDecl *FD =
+ llvm::dyn_cast<clang::FunctionDecl>(*I)) {
+ ValidateFunctionDecl(FD);
+ }
+ }
+ }
+}
+
+
+void RSCheckAST::VisitExpr(clang::Expr *E) {
+ // This is where FS checks for code using pointer and/or 64-bit expressions
+ // (i.e. things like casts).
+
+ // First we skip implicit casts (things like function calls and explicit
+ // array accesses rely heavily on them and they are valid.
+ E = E->IgnoreImpCasts();
+ if (mIsFilterscript &&
+ !SlangRS::IsLocInRSHeaderFile(E->getExprLoc(), mSM) &&
+ !RSExportType::ValidateType(C, E->getType(), NULL, E->getExprLoc(),
+ mTargetAPI, mIsFilterscript)) {
+ mValid = false;
+ } else {
+ // Only visit sub-expressions if we haven't already seen a violation.
+ VisitStmt(E);
+ }
+}
+
+
+bool RSCheckAST::Validate() {
+ clang::TranslationUnitDecl *TUDecl = C.getTranslationUnitDecl();
+ for (clang::DeclContext::decl_iterator DI = TUDecl->decls_begin(),
+ DE = TUDecl->decls_end();
+ DI != DE;
+ DI++) {
+ if (!SlangRS::IsLocInRSHeaderFile(DI->getLocStart(), mSM)) {
+ if (clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(*DI)) {
+ ValidateVarDecl(VD);
+ } else if (clang::FunctionDecl *FD =
+ llvm::dyn_cast<clang::FunctionDecl>(*DI)) {
+ ValidateFunctionDecl(FD);
+ } else if (clang::Stmt *Body = (*DI)->getBody()) {
+ Visit(Body);
+ }
+ }
+ }
+
+ return mValid;
+}
+
+} // namespace slang
diff --git a/slang_rs_check_ast.h b/slang_rs_check_ast.h
new file mode 100644
index 0000000..6a76360
--- /dev/null
+++ b/slang_rs_check_ast.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_ // NOLINT
+#define _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_
+
+#include "slang_assert.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/StmtVisitor.h"
+
+namespace slang {
+
+// This class is designed to walk a Renderscript/Filterscript AST looking for
+// violations. Examples of violations for FS are pointer declarations and
+// casts (i.e. no pointers allowed in FS whatsoever).
+class RSCheckAST : public clang::StmtVisitor<RSCheckAST> {
+ private:
+ clang::ASTContext &C;
+ clang::DiagnosticsEngine &mDiagEngine;
+ clang::SourceManager &mSM;
+ bool mValid;
+ unsigned int mTargetAPI;
+ bool mIsFilterscript;
+
+ public:
+ explicit RSCheckAST(clang::ASTContext &Con, unsigned int TargetAPI,
+ bool IsFilterscript)
+ : C(Con), mDiagEngine(Con.getDiagnostics()), mSM(C.getSourceManager()),
+ mValid(true), mTargetAPI(TargetAPI), mIsFilterscript(IsFilterscript) {
+ return;
+ }
+
+ void VisitStmt(clang::Stmt *S);
+
+ void VisitExpr(clang::Expr *E);
+
+ void VisitDeclStmt(clang::DeclStmt *DS);
+
+ void ValidateFunctionDecl(clang::FunctionDecl *FD);
+
+ void ValidateVarDecl(clang::VarDecl *VD);
+
+ bool Validate();
+};
+
+} // namespace slang
+
+#endif // _FRAMEWORKS_COMPILE_SLANG_SLANG_RS_CHECK_AST_H_ NOLINT
diff --git a/slang_rs_export_type.cpp b/slang_rs_export_type.cpp
index 2d35ef6..94bd6bb 100644
--- a/slang_rs_export_type.cpp
+++ b/slang_rs_export_type.cpp
@@ -89,7 +89,7 @@
const clang::RecordDecl *TopLevelRecord);
static void ReportTypeError(clang::DiagnosticsEngine *DiagEngine,
- const clang::VarDecl *VD,
+ const clang::NamedDecl *ND,
const clang::RecordDecl *TopLevelRecord,
const char *Message,
unsigned int TargetAPI = 0) {
@@ -107,11 +107,11 @@
clang::FullSourceLoc(TopLevelRecord->getLocation(), SM),
DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, Message))
<< TopLevelRecord->getName() << TargetAPI;
- } else if (VD) {
+ } else if (ND) {
DiagEngine->Report(
- clang::FullSourceLoc(VD->getLocation(), SM),
+ clang::FullSourceLoc(ND->getLocation(), SM),
DiagEngine->getCustomDiagID(clang::DiagnosticsEngine::Error, Message))
- << VD->getName() << TargetAPI;
+ << ND->getName() << TargetAPI;
} else {
slangAssert(false && "Variables should be validated before exporting");
}
@@ -337,7 +337,7 @@
// Only if we are already in a composite type (like an array or structure).
if (InCompositeType) {
// Only if we are actually exported (i.e. non-static).
- if (VD->getLinkage() == clang::ExternalLinkage) {
+ if (VD->hasLinkage() && (VD->getLinkage() == clang::ExternalLinkage)) {
// Only if we are not a pointer to an object.
const clang::Type *T = GET_CANONICAL_TYPE(VD->getType().getTypePtr());
if (T->getTypeClass() != clang::Type::Pointer) {
@@ -355,25 +355,30 @@
return true;
}
-// Helper function for ValidateVarDecl(). We do a recursive descent on the
+// Helper function for ValidateType(). We do a recursive descent on the
// type hierarchy to ensure that we can properly export/handle the
// declaration.
// \return true if the variable declaration is valid,
// false if it is invalid (along with proper diagnostics).
//
-// VD - top-level variable declaration that we are validating.
-// T - sub-type of VD's type that we are validating.
+// C - ASTContext (for diagnostics + builtin types).
+// T - sub-type that we are validating.
+// ND - (optional) top-level named declaration that we are validating.
// SPS - set of types we have already seen/validated.
// InCompositeType - true if we are within an outer composite type.
// UnionDecl - set if we are in a sub-type of a union.
// TargetAPI - target SDK API level.
-static bool ValidateVarDeclHelper(
- clang::VarDecl *VD,
+// IsFilterscript - whether or not we are compiling for Filterscript
+static bool ValidateTypeHelper(
+ clang::ASTContext &C,
const clang::Type *&T,
+ clang::NamedDecl *ND,
+ clang::SourceLocation Loc,
llvm::SmallPtrSet<const clang::Type*, 8>& SPS,
bool InCompositeType,
clang::RecordDecl *UnionDecl,
- unsigned int TargetAPI) {
+ unsigned int TargetAPI,
+ bool IsFilterscript) {
if ((T = GET_CANONICAL_TYPE(T)) == NULL)
return true;
@@ -383,7 +388,8 @@
switch (T->getTypeClass()) {
case clang::Type::Record: {
if (RSExportPrimitiveType::IsRSObjectType(T)) {
- if (!ValidateRSObjectInVarDecl(VD, InCompositeType, TargetAPI)) {
+ clang::VarDecl *VD = (ND ? llvm::dyn_cast<clang::VarDecl>(ND) : NULL);
+ if (VD && !ValidateRSObjectInVarDecl(VD, InCompositeType, TargetAPI)) {
return false;
}
}
@@ -393,8 +399,7 @@
if (!UnionDecl) {
return true;
} else if (RSExportPrimitiveType::IsRSObjectType(T)) {
- clang::ASTContext &C = VD->getASTContext();
- ReportTypeError(&C.getDiagnostics(), VD, UnionDecl,
+ ReportTypeError(&C.getDiagnostics(), NULL, UnionDecl,
"unions containing RS object types are not allowed");
return false;
}
@@ -437,7 +442,8 @@
const clang::Type *FT = RSExportType::GetTypeOfDecl(FD);
FT = GET_CANONICAL_TYPE(FT);
- if (!ValidateVarDeclHelper(VD, FT, SPS, true, UnionDecl, TargetAPI)) {
+ if (!ValidateTypeHelper(C, FT, ND, Loc, SPS, true, UnionDecl,
+ TargetAPI, IsFilterscript)) {
return false;
}
}
@@ -446,16 +452,58 @@
}
case clang::Type::Builtin: {
+ if (IsFilterscript) {
+ clang::QualType QT = T->getCanonicalTypeInternal();
+ if (QT == C.DoubleTy ||
+ QT == C.LongDoubleTy ||
+ QT == C.LongTy ||
+ QT == C.LongLongTy) {
+ clang::DiagnosticsEngine &DiagEngine = C.getDiagnostics();
+ if (ND) {
+ DiagEngine.Report(
+ clang::FullSourceLoc(Loc, C.getSourceManager()),
+ DiagEngine.getCustomDiagID(
+ clang::DiagnosticsEngine::Error,
+ "Builtin types > 32 bits in size are forbidden in "
+ "Filterscript: '%0'")) << ND->getName();
+ } else {
+ DiagEngine.Report(
+ clang::FullSourceLoc(Loc, C.getSourceManager()),
+ DiagEngine.getCustomDiagID(
+ clang::DiagnosticsEngine::Error,
+ "Builtin types > 32 bits in size are forbidden in "
+ "Filterscript"));
+ }
+ return false;
+ }
+ }
break;
}
case clang::Type::Pointer: {
+ if (IsFilterscript) {
+ if (ND) {
+ clang::DiagnosticsEngine &DiagEngine = C.getDiagnostics();
+ DiagEngine.Report(
+ clang::FullSourceLoc(Loc, C.getSourceManager()),
+ DiagEngine.getCustomDiagID(
+ clang::DiagnosticsEngine::Error,
+ "Pointers are forbidden in Filterscript: '%0'")) << ND->getName();
+ return false;
+ } else {
+ // TODO(srhines): Find a better way to handle expressions (i.e. no
+ // NamedDecl) involving pointers in FS that should be allowed.
+ // An example would be calls to library functions like
+ // rsMatrixMultiply() that take rs_matrixNxN * types.
+ }
+ }
+
const clang::PointerType *PT =
UNSAFE_CAST_TYPE(const clang::PointerType, T);
const clang::Type *PointeeType = GET_POINTEE_TYPE(PT);
- return ValidateVarDeclHelper(VD, PointeeType, SPS, InCompositeType,
- UnionDecl, TargetAPI);
+ return ValidateTypeHelper(C, PointeeType, ND, Loc, SPS, InCompositeType,
+ UnionDecl, TargetAPI, IsFilterscript);
}
case clang::Type::ExtVector: {
@@ -465,22 +513,21 @@
if (TargetAPI < SLANG_ICS_TARGET_API &&
InCompositeType &&
EVT->getNumElements() == 3) {
- clang::ASTContext &C = VD->getASTContext();
- ReportTypeError(&C.getDiagnostics(), VD, NULL,
+ ReportTypeError(&C.getDiagnostics(), ND, NULL,
"structs containing vectors of dimension 3 cannot "
"be exported at this API level: '%0'");
return false;
}
- return ValidateVarDeclHelper(VD, ElementType, SPS, true, UnionDecl,
- TargetAPI);
+ return ValidateTypeHelper(C, ElementType, ND, Loc, SPS, true, UnionDecl,
+ TargetAPI, IsFilterscript);
}
case clang::Type::ConstantArray: {
const clang::ConstantArrayType *CAT =
UNSAFE_CAST_TYPE(const clang::ConstantArrayType, T);
const clang::Type *ElementType = GET_CONSTANT_ARRAY_ELEMENT_TYPE(CAT);
- return ValidateVarDeclHelper(VD, ElementType, SPS, true, UnionDecl,
- TargetAPI);
+ return ValidateTypeHelper(C, ElementType, ND, Loc, SPS, true, UnionDecl,
+ TargetAPI, IsFilterscript);
}
default: {
@@ -523,12 +570,22 @@
return true;
}
-bool RSExportType::ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI) {
- const clang::Type *T = VD->getType().getTypePtr();
+bool RSExportType::ValidateType(clang::ASTContext &C, clang::QualType QT,
+ clang::NamedDecl *ND, clang::SourceLocation Loc, unsigned int TargetAPI,
+ bool IsFilterscript) {
+ const clang::Type *T = QT.getTypePtr();
llvm::SmallPtrSet<const clang::Type*, 8> SPS =
llvm::SmallPtrSet<const clang::Type*, 8>();
- return ValidateVarDeclHelper(VD, T, SPS, false, NULL, TargetAPI);
+ return ValidateTypeHelper(C, T, ND, Loc, SPS, false, NULL, TargetAPI,
+ IsFilterscript);
+ return true;
+}
+
+bool RSExportType::ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI,
+ bool IsFilterscript) {
+ return ValidateType(VD->getASTContext(), VD->getType(), VD,
+ VD->getLocation(), TargetAPI, IsFilterscript);
}
const clang::Type
diff --git a/slang_rs_export_type.h b/slang_rs_export_type.h
index ea1a885..e9af953 100644
--- a/slang_rs_export_type.h
+++ b/slang_rs_export_type.h
@@ -157,9 +157,18 @@
clang::DiagnosticsEngine *Diags,
const clang::VarDecl *VD);
+ // This function checks whether the specified type can be handled by RS/FS.
+ // If it cannot, this function returns false. Otherwise it returns true.
+ // Filterscript has additional restrictions on supported types.
+ static bool ValidateType(clang::ASTContext &C, clang::QualType QT,
+ clang::NamedDecl *ND, clang::SourceLocation Loc,
+ unsigned int TargetAPI, bool IsFilterscript);
+
// This function ensures that the VarDecl can be properly handled by RS.
// If it cannot, this function returns false. Otherwise it returns true.
- static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI);
+ // Filterscript has additional restrictions on supported types.
+ static bool ValidateVarDecl(clang::VarDecl *VD, unsigned int TargetAPI,
+ bool IsFilterscript);
// @T may not be normalized
static RSExportType *Create(RSContext *Context, const clang::Type *T);
diff --git a/tests/F_fs_oldkernel/fs_oldkernel.fs b/tests/F_fs_oldkernel/fs_oldkernel.fs
new file mode 100644
index 0000000..4f607a5
--- /dev/null
+++ b/tests/F_fs_oldkernel/fs_oldkernel.fs
@@ -0,0 +1,10 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+void old_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+}
+
+uint32_t* bad_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+ return 0;
+}
+
diff --git a/tests/F_fs_oldkernel/stderr.txt.expect b/tests/F_fs_oldkernel/stderr.txt.expect
new file mode 100644
index 0000000..5b53b56
--- /dev/null
+++ b/tests/F_fs_oldkernel/stderr.txt.expect
@@ -0,0 +1,3 @@
+fs_oldkernel.fs:4:17: error: Pointers are forbidden in Filterscript: 'ain'
+fs_oldkernel.fs:7:1: error: Pointers are forbidden in Filterscript: 'bad_kernel'
+fs_oldkernel.fs:7:22: error: Pointers are forbidden in Filterscript: 'ain'
diff --git a/tests/F_fs_oldkernel/stdout.txt.expect b/tests/F_fs_oldkernel/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_oldkernel/stdout.txt.expect
diff --git a/tests/F_fs_ptr/fs_ptr.fs b/tests/F_fs_ptr/fs_ptr.fs
new file mode 100644
index 0000000..7ae49eb
--- /dev/null
+++ b/tests/F_fs_ptr/fs_ptr.fs
@@ -0,0 +1,39 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int *i;
+
+struct f {
+ int i;
+ float *pf;
+ char c;
+ short *ps;
+};
+
+int ia[10];
+
+int __attribute__((kernel)) root(uint32_t ain) {
+ char *c;
+
+ c = (char*) ain; // TODO(srhines): This is ok today.
+ return 0;
+}
+
+void __attribute__((kernel)) in_only(uint32_t ain) {
+}
+
+int __attribute__((kernel)) out_only() {
+ return 0;
+}
+
+int __attribute__((kernel)) everything(uint32_t ain, uint32_t x, uint32_t y) {
+ return (int)&ain; // TODO(srhines): This is ok today.
+}
+
+void old_kernel(const uint32_t *ain, uint32_t x, uint32_t y) {
+}
+
+void test_call() {
+ int i = root(ia[4]);
+}
+
diff --git a/tests/F_fs_ptr/stderr.txt.expect b/tests/F_fs_ptr/stderr.txt.expect
new file mode 100644
index 0000000..2ff21f9
--- /dev/null
+++ b/tests/F_fs_ptr/stderr.txt.expect
@@ -0,0 +1,3 @@
+fs_ptr.fs:4:6: error: Pointers are forbidden in Filterscript: 'i'
+fs_ptr.fs:16:9: error: Pointers are forbidden in Filterscript: 'c'
+fs_ptr.fs:33:17: error: Pointers are forbidden in Filterscript: 'ain'
diff --git a/tests/F_fs_ptr/stdout.txt.expect b/tests/F_fs_ptr/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_ptr/stdout.txt.expect
diff --git a/tests/F_fs_types/fs_types.fs b/tests/F_fs_types/fs_types.fs
new file mode 100644
index 0000000..12c22ce
--- /dev/null
+++ b/tests/F_fs_types/fs_types.fs
@@ -0,0 +1,38 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+double d;
+
+struct s {
+ int i;
+ double d;
+ char c;
+ long l;
+};
+
+struct s myS;
+
+void foo_d(double d) {
+ double e;
+ float f = 0.0;
+ e = d;
+}
+
+void foo_l(long l) {
+ long m;
+ int i = 1l;
+ m = l;
+}
+
+void foo_ll(long long l) {
+ long long m;
+ int i = 1ll;
+ m = l;
+}
+
+void foo_ld(long double l) {
+ long double m;
+ float f = 0.0L;
+ m = l;
+}
+
diff --git a/tests/F_fs_types/stderr.txt.expect b/tests/F_fs_types/stderr.txt.expect
new file mode 100644
index 0000000..9422d76
--- /dev/null
+++ b/tests/F_fs_types/stderr.txt.expect
@@ -0,0 +1,18 @@
+fs_types.fs:4:8: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'd'
+fs_types.fs:13:10: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'myS'
+fs_types.fs:15:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'd'
+fs_types.fs:16:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'e'
+fs_types.fs:17:15: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:18:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:21:12: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:22:10: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:23:13: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:24:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:27:13: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:28:15: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:29:13: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:30:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:33:13: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'l'
+fs_types.fs:34:17: error: Builtin types > 32 bits in size are forbidden in Filterscript: 'm'
+fs_types.fs:35:15: error: Builtin types > 32 bits in size are forbidden in Filterscript
+fs_types.fs:36:7: error: Builtin types > 32 bits in size are forbidden in Filterscript
diff --git a/tests/F_fs_types/stdout.txt.expect b/tests/F_fs_types/stdout.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/F_fs_types/stdout.txt.expect
diff --git a/tests/F_union/stderr.txt.expect b/tests/F_union/stderr.txt.expect
index 1848917..2bf177d 100644
--- a/tests/F_union/stderr.txt.expect
+++ b/tests/F_union/stderr.txt.expect
@@ -1,2 +1,3 @@
union.rs:4:7: error: unions cannot be exported: 'u'
union.rs:9:14: error: unions containing RS object types are not allowed
+union.rs:16:11: error: unions containing RS object types are not allowed
diff --git a/tests/F_union/union.rs b/tests/F_union/union.rs
index aee4948..c6d423c 100644
--- a/tests/F_union/union.rs
+++ b/tests/F_union/union.rs
@@ -11,3 +11,11 @@
rs_font f;
} myUnion2;
+
+void foo() {
+ union iu {
+ rs_font f;
+ int i;
+ } v;
+ (void) v;
+}
diff --git a/tests/P_fs_kernel/fs_kernel.fs b/tests/P_fs_kernel/fs_kernel.fs
new file mode 100644
index 0000000..d8f0c4d
--- /dev/null
+++ b/tests/P_fs_kernel/fs_kernel.fs
@@ -0,0 +1,18 @@
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+int __attribute__((kernel)) root(uint32_t ain) {
+ return 0;
+}
+
+void __attribute__((kernel)) in_only(uint32_t ain) {
+}
+
+int __attribute__((kernel)) out_only() {
+ return 0;
+}
+
+int __attribute__((kernel)) everything(uint32_t ain, uint32_t x, uint32_t y) {
+ return 0;
+}
+
diff --git a/tests/P_fs_kernel/stderr.txt.expect b/tests/P_fs_kernel/stderr.txt.expect
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/P_fs_kernel/stderr.txt.expect
diff --git a/tests/P_fs_kernel/stdout.txt.expect b/tests/P_fs_kernel/stdout.txt.expect
new file mode 100644
index 0000000..4a8eafd
--- /dev/null
+++ b/tests/P_fs_kernel/stdout.txt.expect
@@ -0,0 +1 @@
+Generating ScriptC_fs_kernel.java ...
diff --git a/tests/test.py b/tests/test.py
index 44327e8..7392cf6 100755
--- a/tests/test.py
+++ b/tests/test.py
@@ -75,6 +75,8 @@
'-I ../../../../../external/clang/lib/Headers/')
base_args = cmd_string.split()
rs_files = glob.glob('*.rs')
+ fs_files = glob.glob('*.fs')
+ rs_files += fs_files;
rs_files.sort()
# Extra command line arguments can be placed as // comments at the start of