Commit 476541f6 by kbr@chromium.org

Implemented new restrictions on nesting of structs in WebGL shaders.

Added previously missing check for embedded structs; even though these attempts would be caught by an underlying GLSL compiler, the shader validator should not let them through. BUG=http://code.google.com/p/angleproject/issues/detail?id=235 TEST=WebGL conformance tests Review URL: http://codereview.appspot.com/5327046 git-svn-id: https://angleproject.googlecode.com/svn/trunk@809 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent c5a7b690
#define MAJOR_VERSION 0 #define MAJOR_VERSION 0
#define MINOR_VERSION 0 #define MINOR_VERSION 0
#define BUILD_VERSION 0 #define BUILD_VERSION 0
#define BUILD_REVISION 808 #define BUILD_REVISION 809
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x) #define MACRO_STRINGIFY(x) STRINGIFY(x)
......
...@@ -1425,6 +1425,53 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n ...@@ -1425,6 +1425,53 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n
return typedNode; return typedNode;
} }
bool TParseContext::enterStructDeclaration(int line, const TString& identifier)
{
++structNestingLevel;
// Embedded structure definitions are not supported per GLSL ES spec.
// They aren't allowed in GLSL either, but we need to detect this here
// so we don't rely on the GLSL compiler to catch it.
if (structNestingLevel > 1) {
error(line, "", "Embedded struct definitions are not allowed", "");
return true;
}
return false;
}
void TParseContext::exitStructDeclaration()
{
--structNestingLevel;
}
namespace {
const int kWebGLMaxStructNesting = 4;
} // namespace
bool TParseContext::structNestingErrorCheck(TSourceLoc line, const TType& fieldType)
{
if (shaderSpec != SH_WEBGL_SPEC) {
return false;
}
if (fieldType.getBasicType() != EbtStruct) {
return false;
}
// We're already inside a structure definition at this point, so add
// one to the field's struct nesting.
if (1 + fieldType.getDeepestStructNesting() >= kWebGLMaxStructNesting) {
error(line, "", "", "Reference of struct type %s exceeds maximum struct nesting of %d",
fieldType.getTypeName().c_str(), kWebGLMaxStructNesting);
return true;
}
return false;
}
// //
// Parse an array of strings using yyparse. // Parse an array of strings using yyparse.
// //
......
...@@ -31,9 +31,25 @@ struct TPragma { ...@@ -31,9 +31,25 @@ struct TPragma {
// //
struct TParseContext { struct TParseContext {
TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, bool checksPrecErrors, const char* sourcePath, TInfoSink& is) : TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, bool checksPrecErrors, const char* sourcePath, TInfoSink& is) :
intermediate(interm), symbolTable(symt), extensionBehavior(ext), infoSink(is), shaderType(type), shaderSpec(spec), compileOptions(options), checksPrecisionErrors(checksPrecErrors), sourcePath(sourcePath), treeRoot(0), intermediate(interm),
numErrors(0), lexAfterType(false), loopNestingLevel(0), symbolTable(symt),
inTypeParen(false), contextPragma(true, false), scanner(NULL) { } extensionBehavior(ext),
infoSink(is),
shaderType(type),
shaderSpec(spec),
compileOptions(options),
sourcePath(sourcePath),
treeRoot(0),
numErrors(0),
lexAfterType(false),
loopNestingLevel(0),
structNestingLevel(0),
inTypeParen(false),
currentFunctionType(NULL),
functionReturnsValue(false),
checksPrecisionErrors(checksPrecErrors),
contextPragma(true, false),
scanner(NULL) { }
TIntermediate& intermediate; // to hold and build a parse tree TIntermediate& intermediate; // to hold and build a parse tree
TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed
TExtensionBehavior& extensionBehavior; // mapping between supported extensions and current behavior. TExtensionBehavior& extensionBehavior; // mapping between supported extensions and current behavior.
...@@ -46,6 +62,7 @@ struct TParseContext { ...@@ -46,6 +62,7 @@ struct TParseContext {
int numErrors; int numErrors;
bool lexAfterType; // true if we've recognized a type, so can only be looking for an identifier bool lexAfterType; // true if we've recognized a type, so can only be looking for an identifier
int loopNestingLevel; // 0 if outside all loops int loopNestingLevel; // 0 if outside all loops
int structNestingLevel; // incremented while parsing a struct declaration
bool inTypeParen; // true if in parentheses, looking only for an identifier bool inTypeParen; // true if in parentheses, looking only for an identifier
const TType* currentFunctionType; // the return type of the function that's currently being parsed const TType* currentFunctionType; // the return type of the function that's currently being parsed
bool functionReturnsValue; // true if a non-void function has a return bool functionReturnsValue; // true if a non-void function has a return
...@@ -105,6 +122,14 @@ struct TParseContext { ...@@ -105,6 +122,14 @@ struct TParseContext {
TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc); TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc);
TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, TSourceLoc line); TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, TSourceLoc line);
TIntermTyped* addConstStruct(TString& , TIntermTyped*, TSourceLoc); TIntermTyped* addConstStruct(TString& , TIntermTyped*, TSourceLoc);
// Performs an error check for embedded struct declarations.
// Returns true if an error was raised due to the declaration of
// this struct.
bool enterStructDeclaration(TSourceLoc line, const TString& identifier);
void exitStructDeclaration();
bool structNestingErrorCheck(TSourceLoc line, const TType& fieldType);
}; };
int PaParseStrings(int count, const char* const string[], const int length[], int PaParseStrings(int count, const char* const string[], const int length[],
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#include <stdio.h> #include <stdio.h>
#include <algorithm>
// //
// TType helper function needs a place to live. // TType helper function needs a place to live.
// //
...@@ -71,6 +73,20 @@ int TType::getStructSize() const ...@@ -71,6 +73,20 @@ int TType::getStructSize() const
return structureSize; return structureSize;
} }
void TType::computeDeepestStructNesting()
{
if (!getStruct()) {
return;
}
int maxNesting = 0;
for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); ++tl) {
maxNesting = std::max(maxNesting, ((*tl).type)->getDeepestStructNesting());
}
deepestStructNesting = 1 + maxNesting;
}
// //
// Dump functions. // Dump functions.
// //
......
...@@ -85,21 +85,22 @@ public: ...@@ -85,21 +85,22 @@ public:
TType() {} TType() {}
TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary, int s = 1, bool m = false, bool a = false) : TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary, int s = 1, bool m = false, bool a = false) :
type(t), precision(p), qualifier(q), size(s), matrix(m), array(a), arraySize(0), type(t), precision(p), qualifier(q), size(s), matrix(m), array(a), arraySize(0),
maxArraySize(0), arrayInformationType(0), structure(0), structureSize(0), fieldName(0), mangled(0), typeName(0) maxArraySize(0), arrayInformationType(0), structure(0), structureSize(0), deepestStructNesting(0), fieldName(0), mangled(0), typeName(0)
{ {
} }
explicit TType(const TPublicType &p) : explicit TType(const TPublicType &p) :
type(p.type), precision(p.precision), qualifier(p.qualifier), size(p.size), matrix(p.matrix), array(p.array), arraySize(p.arraySize), type(p.type), precision(p.precision), qualifier(p.qualifier), size(p.size), matrix(p.matrix), array(p.array), arraySize(p.arraySize),
maxArraySize(0), arrayInformationType(0), structure(0), structureSize(0), fieldName(0), mangled(0), typeName(0) maxArraySize(0), arrayInformationType(0), structure(0), structureSize(0), deepestStructNesting(0), fieldName(0), mangled(0), typeName(0)
{ {
if (p.userDef) { if (p.userDef) {
structure = p.userDef->getStruct(); structure = p.userDef->getStruct();
typeName = NewPoolTString(p.userDef->getTypeName().c_str()); typeName = NewPoolTString(p.userDef->getTypeName().c_str());
computeDeepestStructNesting();
} }
} }
TType(TTypeList* userDef, const TString& n, TPrecision p = EbpUndefined) : TType(TTypeList* userDef, const TString& n, TPrecision p = EbpUndefined) :
type(EbtStruct), precision(p), qualifier(EvqTemporary), size(1), matrix(false), array(false), arraySize(0), type(EbtStruct), precision(p), qualifier(EvqTemporary), size(1), matrix(false), array(false), arraySize(0),
maxArraySize(0), arrayInformationType(0), structure(userDef), structureSize(0), fieldName(0), mangled(0) maxArraySize(0), arrayInformationType(0), structure(userDef), structureSize(0), deepestStructNesting(0), fieldName(0), mangled(0)
{ {
typeName = NewPoolTString(n.c_str()); typeName = NewPoolTString(n.c_str());
} }
...@@ -144,6 +145,7 @@ public: ...@@ -144,6 +145,7 @@ public:
structureSize = copyOf.structureSize; structureSize = copyOf.structureSize;
maxArraySize = copyOf.maxArraySize; maxArraySize = copyOf.maxArraySize;
deepestStructNesting = copyOf.deepestStructNesting;
assert(copyOf.arrayInformationType == 0); assert(copyOf.arrayInformationType == 0);
arrayInformationType = 0; // arrayInformationType should not be set for builtIn symbol table level arrayInformationType = 0; // arrayInformationType should not be set for builtIn symbol table level
} }
...@@ -202,7 +204,7 @@ public: ...@@ -202,7 +204,7 @@ public:
bool isScalar() const { return size == 1 && !matrix && !structure; } bool isScalar() const { return size == 1 && !matrix && !structure; }
TTypeList* getStruct() const { return structure; } TTypeList* getStruct() const { return structure; }
void setStruct(TTypeList* s) { structure = s; } void setStruct(TTypeList* s) { structure = s; computeDeepestStructNesting(); }
const TString& getTypeName() const const TString& getTypeName() const
{ {
...@@ -268,9 +270,24 @@ public: ...@@ -268,9 +270,24 @@ public:
const char* getQualifierString() const { return ::getQualifierString(qualifier); } const char* getQualifierString() const { return ::getQualifierString(qualifier); }
TString getCompleteString() const; TString getCompleteString() const;
// If this type is a struct, returns the deepest struct nesting of
// any field in the struct. For example:
// struct nesting1 {
// vec4 position;
// };
// struct nesting2 {
// nesting1 field1;
// vec4 field2;
// };
// For type "nesting2", this method would return 2 -- the number
// of structures through which indirection must occur to reach the
// deepest field (nesting2.field1.position).
int getDeepestStructNesting() const { return deepestStructNesting; }
protected: protected:
void buildMangledName(TString&); void buildMangledName(TString&);
int getStructSize() const; int getStructSize() const;
void computeDeepestStructNesting();
TBasicType type : 6; TBasicType type : 6;
TPrecision precision; TPrecision precision;
...@@ -284,6 +301,7 @@ protected: ...@@ -284,6 +301,7 @@ protected:
TTypeList* structure; // 0 unless this is a struct TTypeList* structure; // 0 unless this is a struct
mutable int structureSize; mutable int structureSize;
int deepestStructNesting;
TString *fieldName; // for structure field names TString *fieldName; // for structure field names
TString *mangled; TString *mangled;
......
...@@ -1642,11 +1642,11 @@ type_specifier_nonarray ...@@ -1642,11 +1642,11 @@ type_specifier_nonarray
; ;
struct_specifier struct_specifier
: STRUCT IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE { : STRUCT IDENTIFIER LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
if (context->reservedErrorCheck($2.line, *$2.string)) if (context->reservedErrorCheck($2.line, *$2.string))
context->recover(); context->recover();
TType* structure = new TType($4, *$2.string); TType* structure = new TType($5, *$2.string);
TVariable* userTypeDef = new TVariable($2.string, *structure, true); TVariable* userTypeDef = new TVariable($2.string, *structure, true);
if (! context->symbolTable.insert(*userTypeDef)) { if (! context->symbolTable.insert(*userTypeDef)) {
context->error($2.line, "redefinition", $2.string->c_str(), "struct"); context->error($2.line, "redefinition", $2.string->c_str(), "struct");
...@@ -1654,11 +1654,13 @@ struct_specifier ...@@ -1654,11 +1654,13 @@ struct_specifier
} }
$$.setBasic(EbtStruct, EvqTemporary, $1.line); $$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure; $$.userDef = structure;
context->exitStructDeclaration();
} }
| STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE { | STRUCT LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
TType* structure = new TType($3, TString("")); TType* structure = new TType($4, TString(""));
$$.setBasic(EbtStruct, EvqTemporary, $1.line); $$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure; $$.userDef = structure;
context->exitStructDeclaration();
} }
; ;
...@@ -1708,6 +1710,10 @@ struct_declaration ...@@ -1708,6 +1710,10 @@ struct_declaration
type->setStruct($1.userDef->getStruct()); type->setStruct($1.userDef->getStruct());
type->setTypeName($1.userDef->getTypeName()); type->setTypeName($1.userDef->getTypeName());
} }
if (context->structNestingErrorCheck($1.line, *type)) {
context->recover();
}
} }
} }
; ;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment