@@ -39,6 +39,7 @@ function Validator(opts) {
3939
4040 // Load rules
4141 this . rules = loadRules ( ) ;
42+ this . cache = new Map ( ) ;
4243}
4344
4445/**
@@ -67,12 +68,14 @@ Validator.prototype.compile = function(schema) {
6768 }
6869
6970 const rules = this . compileSchemaType ( schema ) ;
71+ this . cache . clear ( ) ;
7072 return function ( value ) {
7173 return self . checkSchemaType ( value , rules , undefined , null ) ;
7274 } ;
7375 }
7476
7577 const rule = this . compileSchemaObject ( schema ) ;
78+ this . cache . clear ( ) ;
7679 return function ( value ) {
7780 return self . checkSchemaObject ( value , rule , undefined , null ) ;
7881 } ;
@@ -83,26 +86,32 @@ Validator.prototype.compileSchemaObject = function(schemaObject) {
8386 throw new Error ( "Invalid schema!" ) ;
8487 }
8588
86- const compiledObject = Object . keys ( schemaObject ) . map ( name => {
89+ let compiledObject = this . cache . get ( schemaObject ) ;
90+ if ( compiledObject ) {
91+ compiledObject . cycle = true ;
92+ return compiledObject ;
93+ } else {
94+ compiledObject = { cycle : false , properties : null , compiledObjectFunction : null , objectStack : [ ] } ;
95+ this . cache . set ( schemaObject , compiledObject ) ;
96+ }
97+
98+ compiledObject . properties = Object . keys ( schemaObject ) . map ( name => {
8799 const compiledType = this . compileSchemaType ( schemaObject [ name ] ) ;
88100 return { name : name , compiledType : compiledType } ;
89101 } ) ;
90102
91- // Uncomment this line to use compiled object validator:
92- // return compiledObject;
93-
94103 const sourceCode = [ ] ;
95104 sourceCode . push ( "let res;" ) ;
96105 sourceCode . push ( "let propertyPath;" ) ;
97106 sourceCode . push ( "const errors = [];" ) ;
98- for ( let i = 0 ; i < compiledObject . length ; i ++ ) {
99- const property = compiledObject [ i ] ;
107+ for ( let i = 0 ; i < compiledObject . properties . length ; i ++ ) {
108+ const property = compiledObject . properties [ i ] ;
100109 const name = property . name ;
101110 sourceCode . push ( `propertyPath = (path !== undefined ? path + ".${ name } " : "${ name } ");` ) ;
102111 if ( Array . isArray ( property . compiledType ) ) {
103- sourceCode . push ( `res = this.checkSchemaType(value.${ name } , compiledObject [${ i } ].compiledType, propertyPath, value);` ) ;
112+ sourceCode . push ( `res = this.checkSchemaType(value.${ name } , properties [${ i } ].compiledType, propertyPath, value);` ) ;
104113 } else {
105- sourceCode . push ( `res = this.checkSchemaRule(value.${ name } , compiledObject [${ i } ].compiledType, propertyPath, value);` ) ;
114+ sourceCode . push ( `res = this.checkSchemaRule(value.${ name } , properties [${ i } ].compiledType, propertyPath, value);` ) ;
106115 }
107116 sourceCode . push ( "if (res !== true) {" ) ;
108117 sourceCode . push ( "\tthis.handleResult(errors, propertyPath, res);" ) ;
@@ -111,12 +120,9 @@ Validator.prototype.compileSchemaObject = function(schemaObject) {
111120
112121 sourceCode . push ( "return errors.length === 0 ? true : errors;" ) ;
113122
114- const compiledObjectFunction = new Function ( "value" , "compiledObject " , "path" , "parent" , sourceCode . join ( "\n" ) ) ;
123+ compiledObject . compiledObjectFunction = new Function ( "value" , "properties " , "path" , "parent" , sourceCode . join ( "\n" ) ) ;
115124
116- const self = this ;
117- return function ( value , _unused , path , parent ) {
118- return compiledObjectFunction . call ( self , value , compiledObject , path , parent ) ;
119- } ;
125+ return compiledObject ;
120126} ;
121127
122128Validator . prototype . compileSchemaType = function ( schemaType ) {
@@ -165,23 +171,40 @@ Validator.prototype.compileSchemaRule = function(schemaRule) {
165171} ;
166172
167173Validator . prototype . checkSchemaObject = function ( value , compiledObject , path , parent ) {
168- if ( compiledObject instanceof Function ) {
169- return compiledObject ( value , undefined , path , parent ) ;
174+ if ( compiledObject . cycle ) {
175+ if ( compiledObject . objectStack . indexOf ( value ) !== - 1 ) {
176+ return true ;
177+ }
178+
179+ compiledObject . objectStack . push ( value ) ;
180+ const result = this . checkSchemaObjectInner ( value , compiledObject , path , parent ) ;
181+ compiledObject . objectStack . pop ( ) ;
182+ return result ;
183+ } else {
184+ return this . checkSchemaObjectInner ( value , compiledObject , path , parent ) ;
170185 }
186+ } ;
187+
188+ Validator . prototype . checkSchemaObjectInner = function ( value , compiledObject , path , parent ) {
189+ return compiledObject . compiledObjectFunction . call ( this , value , compiledObject . properties , path , parent ) ;
190+
191+ /*
192+ // Reference implementation of the object checker
171193
172194 const errors = [];
173- const checksLength = compiledObject . length ;
174- for ( let i = 0 ; i < checksLength ; i ++ ) {
175- const check = compiledObject [ i ] ;
176- const propertyPath = ( path !== undefined ? path + "." : "" ) + check . name ;
177- const res = this . checkSchemaType ( value [ check . name ] , check . compiledType , propertyPath , value ) ;
195+ const propertiesLength = compiledObject.properties .length;
196+ for (let i = 0; i < propertiesLength ; i++) {
197+ const property = compiledObject.properties [i];
198+ const propertyPath = (path !== undefined ? path + "." : "") + property .name;
199+ const res = this.checkSchemaType(value[property .name], property .compiledType, propertyPath, value);
178200
179201 if (res !== true) {
180202 this.handleResult(errors, propertyPath, res);
181203 }
182204 }
183205
184206 return errors.length === 0 ? true : errors;
207+ */
185208} ;
186209
187210Validator . prototype . checkSchemaType = function ( value , compiledType , path , parent ) {
0 commit comments