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 MINOR_VERSION 0
#define BUILD_VERSION 0
#define BUILD_REVISION 808
#define BUILD_REVISION 809
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
......
......@@ -1425,6 +1425,53 @@ TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* n
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.
//
......
......@@ -31,9 +31,25 @@ struct TPragma {
//
struct TParseContext {
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),
numErrors(0), lexAfterType(false), loopNestingLevel(0),
inTypeParen(false), contextPragma(true, false), scanner(NULL) { }
intermediate(interm),
symbolTable(symt),
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
TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed
TExtensionBehavior& extensionBehavior; // mapping between supported extensions and current behavior.
......@@ -46,6 +62,7 @@ struct TParseContext {
int numErrors;
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 structNestingLevel; // incremented while parsing a struct declaration
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
bool functionReturnsValue; // true if a non-void function has a return
......@@ -105,6 +122,14 @@ struct TParseContext {
TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc);
TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, TSourceLoc line);
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[],
......
......@@ -13,6 +13,8 @@
#include <stdio.h>
#include <algorithm>
//
// TType helper function needs a place to live.
//
......@@ -71,6 +73,20 @@ int TType::getStructSize() const
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.
//
......
......@@ -85,21 +85,22 @@ public:
TType() {}
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),
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) :
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) {
structure = p.userDef->getStruct();
typeName = NewPoolTString(p.userDef->getTypeName().c_str());
computeDeepestStructNesting();
}
}
TType(TTypeList* userDef, const TString& n, TPrecision p = EbpUndefined) :
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());
}
......@@ -144,6 +145,7 @@ public:
structureSize = copyOf.structureSize;
maxArraySize = copyOf.maxArraySize;
deepestStructNesting = copyOf.deepestStructNesting;
assert(copyOf.arrayInformationType == 0);
arrayInformationType = 0; // arrayInformationType should not be set for builtIn symbol table level
}
......@@ -202,7 +204,7 @@ public:
bool isScalar() const { return size == 1 && !matrix && !structure; }
TTypeList* getStruct() const { return structure; }
void setStruct(TTypeList* s) { structure = s; }
void setStruct(TTypeList* s) { structure = s; computeDeepestStructNesting(); }
const TString& getTypeName() const
{
......@@ -268,9 +270,24 @@ public:
const char* getQualifierString() const { return ::getQualifierString(qualifier); }
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:
void buildMangledName(TString&);
int getStructSize() const;
void computeDeepestStructNesting();
TBasicType type : 6;
TPrecision precision;
......@@ -284,6 +301,7 @@ protected:
TTypeList* structure; // 0 unless this is a struct
mutable int structureSize;
int deepestStructNesting;
TString *fieldName; // for structure field names
TString *mangled;
......
......@@ -1642,11 +1642,11 @@ type_specifier_nonarray
;
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))
context->recover();
TType* structure = new TType($4, *$2.string);
TType* structure = new TType($5, *$2.string);
TVariable* userTypeDef = new TVariable($2.string, *structure, true);
if (! context->symbolTable.insert(*userTypeDef)) {
context->error($2.line, "redefinition", $2.string->c_str(), "struct");
......@@ -1654,11 +1654,13 @@ struct_specifier
}
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
context->exitStructDeclaration();
}
| STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE {
TType* structure = new TType($3, TString(""));
| STRUCT LEFT_BRACE { if (context->enterStructDeclaration($2.line, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE {
TType* structure = new TType($4, TString(""));
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
context->exitStructDeclaration();
}
;
......@@ -1708,6 +1710,10 @@ struct_declaration
type->setStruct($1.userDef->getStruct());
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