Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project authors. All rights reserved. | 
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be | 
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. | 
| 5 | 5 | 
| 6 import itertools | |
| 7 import multiprocessing | |
| 8 import optparse | |
| 6 import os | 9 import os | 
| 10 import random | |
| 7 import re | 11 import re | 
| 8 import shutil | 12 import shutil | 
| 13 import signal | |
| 14 import string | |
| 15 import subprocess | |
| 9 import sys | 16 import sys | 
| 17 import time | |
| 10 | 18 | 
| 11 # TODO(jkummerow): Support DATA_VIEW_{G,S}ETTER in runtime.cc | 19 # TODO(jkummerow): Support DATA_VIEW_{G,S}ETTER in runtime.cc | 
| 12 | 20 | 
| 13 FILENAME = "src/runtime.cc" | 21 FILENAME = "src/runtime.cc" | 
| 14 HEADERFILENAME = "src/runtime.h" | 22 HEADERFILENAME = "src/runtime.h" | 
| 15 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") | 23 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") | 
| 16 ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") | 24 ARGSLENGTH = re.compile(".*ASSERT\(.*args\.length\(\) == (\d+)\);") | 
| 17 FUNCTIONEND = "}\n" | 25 FUNCTIONEND = "}\n" | 
| 18 | 26 | 
| 19 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) | 27 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")) | 
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 149 } | 157 } | 
| 150 | 158 | 
| 151 | 159 | 
| 152 # Types of arguments that cannot be generated in a JavaScript testcase. | 160 # Types of arguments that cannot be generated in a JavaScript testcase. | 
| 153 NON_JS_TYPES = [ | 161 NON_JS_TYPES = [ | 
| 154 "Code", "Context", "FixedArray", "FunctionTemplateInfo", | 162 "Code", "Context", "FixedArray", "FunctionTemplateInfo", | 
| 155 "JSFunctionResultCache", "JSMessageObject", "Map", "ScopeInfo", | 163 "JSFunctionResultCache", "JSMessageObject", "Map", "ScopeInfo", | 
| 156 "SharedFunctionInfo"] | 164 "SharedFunctionInfo"] | 
| 157 | 165 | 
| 158 | 166 | 
| 159 # Maps argument types to concrete example inputs of that type. | 167 class Generator(object): | 
| 160 JS_TYPE_GENERATORS = { | 168 | 
| 161 "Boolean": "true", | 169 def RandomVariable(self, varname, vartype, simple): | 
| 162 "HeapObject": "new Object()", | 170 if simple: | 
| 163 "Int32": "32", | 171 return self._Variable(varname, self.GENERATORS[vartype][0]) | 
| 164 "JSArray": "new Array()", | 172 return self.GENERATORS[vartype][1](self, varname, | 
| 165 "JSArrayBuffer": "new ArrayBuffer(8)", | 173 self.DEFAULT_RECURSION_BUDGET) | 
| 166 "JSDataView": "new DataView(new ArrayBuffer(8))", | 174 | 
| 167 "JSDate": "new Date()", | 175 @staticmethod | 
| 168 "JSFunction": "function() {}", | 176 def IsTypeSupported(typename): | 
| 169 "JSFunctionProxy": "Proxy.createFunction({}, function() {})", | 177 return typename in Generator.GENERATORS | 
| 170 "JSGeneratorObject": "(function*(){ yield 1; })()", | 178 | 
| 171 "JSMap": "new Map()", | 179 USUAL_SUSPECT_PROPERTIES = ["size", "length", "byteLength", "__proto__", | 
| 172 "JSMapIterator": "%MapCreateIterator(new Map(), 3)", | 180 "prototype"] | 
| 173 "JSObject": "new Object()", | 181 DEFAULT_RECURSION_BUDGET = 2 | 
| 174 "JSProxy": "Proxy.create({})", | 182 PROXY_TRAPS = """{ | 
| 175 "JSReceiver": "new Object()", | 183 getOwnPropertyDescriptor: function(name) { | 
| 176 "JSRegExp": "/ab/g", | 184 return {value: function() {}, configurable: true, writable: true, | 
| 177 "JSSet": "new Set()", | 185 enumerable: true}; | 
| 178 "JSSetIterator": "%SetCreateIterator(new Set(), 2)", | 186 }, | 
| 179 "JSTypedArray": "new Int32Array(2)", | 187 getPropertyDescriptor: function(name) { | 
| 180 "JSValue": "new String('foo')", | 188 return {value: function() {}, configurable: true, writable: true, | 
| 181 "JSWeakCollection": "new WeakMap()", | 189 enumerable: true}; | 
| 182 "Name": "\"name\"", | 190 }, | 
| 183 "Number": "1.5", | 191 getOwnPropertyNames: function() { return []; }, | 
| 184 "Object": "new Object()", | 192 getPropertyNames: function() { return []; }, | 
| 185 "PropertyDetails": "513", | 193 defineProperty: function(name, descriptor) {}, | 
| 186 "SeqString": "\"seqstring\"", | 194 delete: function(name) { return true; }, | 
| 187 "Smi": 1, | 195 fix: function() {} | 
| 188 "StrictMode": "1", | 196 }""" | 
| 189 "String": "\"foo\"", | 197 | 
| 190 "Symbol": "Symbol(\"symbol\")", | 198 def _Variable(self, name, value, fallback=None): | 
| 191 "Uint32": "32", | 199 args = { "name": name, "value": value, "fallback": fallback } | 
| 192 } | 200 if fallback: | 
| 201 wrapper = "try { %%s } catch(e) { var %(name)s = %(fallback)s; }" % args | |
| 202 else: | |
| 203 wrapper = "%s" | |
| 204 return [wrapper % ("var %(name)s = %(value)s;" % args)] | |
| 205 | |
| 206 def _Boolean(self, name, recursion_budget): | |
| 207 return self._Variable(name, random.choice(["true", "false"])) | |
| 208 | |
| 209 def _Oddball(self, name, recursion_budget): | |
| 210 return self._Variable(name, | |
| 211 random.choice(["true", "false", "undefined", "null"])) | |
| 212 | |
| 213 def _StrictMode(self, name, recursion_budget): | |
| 214 return self._Variable(name, random.choice([0, 1])) | |
| 215 | |
| 216 def _Int32(self, name, recursion_budget=0): | |
| 217 die = random.random() | |
| 218 if die < 0.5: | |
| 219 value = random.choice([-3, -1, 0, 1, 2, 10, 515, 0x3fffffff, 0x7fffffff, | |
| 220 0x40000000, -0x40000000, -0x80000000]) | |
| 221 elif die < 0.75: | |
| 222 value = random.randint(-1000, 1000) | |
| 223 else: | |
| 224 value = random.randint(-0x80000000, 0x7fffffff) | |
| 225 return self._Variable(name, value) | |
| 226 | |
| 227 def _Uint32(self, name, recursion_budget=0): | |
| 228 die = random.random() | |
| 229 if die < 0.5: | |
| 230 value = random.choice([0, 1, 2, 3, 4, 8, 0x3fffffff, 0x40000000, | |
| 231 0x7fffffff, 0xffffffff]) | |
| 232 elif die < 0.75: | |
| 233 value = random.randint(0, 1000) | |
| 234 else: | |
| 235 value = random.randint(0, 0xffffffff) | |
| 236 return self._Variable(name, value) | |
| 237 | |
| 238 def _Smi(self, name, recursion_budget): | |
| 239 die = random.random() | |
| 240 if die < 0.5: | |
| 241 value = random.choice([-5, -1, 0, 1, 2, 3, 0x3fffffff, -0x40000000]) | |
| 242 elif die < 0.75: | |
| 243 value = random.randint(-1000, 1000) | |
| 244 else: | |
| 245 value = random.randint(-0x40000000, 0x3fffffff) | |
| 246 return self._Variable(name, value) | |
| 247 | |
| 248 def _Number(self, name, recursion_budget): | |
| 249 die = random.random() | |
| 250 if die < 0.5: | |
| 251 return self._Smi(name, recursion_budget) | |
| 252 elif die < 0.6: | |
| 253 value = random.choice(["Infinity", "-Infinity", "NaN", "-0"]) | |
| 254 else: | |
| 255 value = random.lognormvariate(0, 15) | |
| 
 
Jarin
2014/05/14 11:55:08
We might also want to add smallest/largest positiv
 
Jakob Kummerow
2014/05/14 12:52:04
Done.
 
 | |
| 256 return self._Variable(name, value) | |
| 257 | |
| 258 def _RawRandomString(self, minlength=0, maxlength=100, | |
| 259 alphabet=string.ascii_letters): | |
| 260 length = random.randint(minlength, maxlength) | |
| 261 result = "" | |
| 262 for i in xrange(length): | |
| 263 result += random.choice(alphabet) | |
| 264 return result | |
| 265 | |
| 266 def _SeqString(self, name, recursion_budget): | |
| 267 s1 = self._RawRandomString(1, 5) | |
| 268 s2 = self._RawRandomString(1, 5) | |
| 269 # 'foo' + 'bar' | |
| 270 return self._Variable(name, "\"%s\" + \"%s\"" % (s1, s2)) | |
| 271 | |
| 272 def _SlicedString(self, name): | |
| 273 s = self._RawRandomString(20, 30) | |
| 274 # 'ffoo12345678901234567890'.substr(1) | |
| 275 return self._Variable(name, "\"%s\".substr(1)" % s) | |
| 276 | |
| 277 def _ConsString(self, name): | |
| 278 s1 = self._RawRandomString(8, 15) | |
| 279 s2 = self._RawRandomString(8, 15) | |
| 280 # 'foo12345' + (function() { return 'bar12345';})() | |
| 281 return self._Variable(name, | |
| 282 "\"%s\" + (function() { return \"%s\";})()" % (s1, s2)) | |
| 283 | |
| 284 def _ExternalString(self, name): | |
| 285 # Needs --expose-externalize-string. | |
| 286 return None | |
| 287 | |
| 288 def _InternalizedString(self, name): | |
| 289 return self._Variable(name, "\"%s\"" % self._RawRandomString(0, 20)) | |
| 290 | |
| 291 def _String(self, name, recursion_budget): | |
| 
 
Jarin
2014/05/14 11:55:08
Would it make sense to generate number strings? (P
 
Jakob Kummerow
2014/05/14 12:52:04
Done.
 
 | |
| 292 die = random.random() | |
| 293 if die < 0.4: | |
| 294 string = random.choice(self.USUAL_SUSPECT_PROPERTIES) | |
| 295 return self._Variable(name, "\"%s\"" % string) | |
| 296 elif die < 0.55: | |
| 297 return self._SeqString(name, recursion_budget) | |
| 298 elif die < 0.7: | |
| 299 return self._ConsString(name) | |
| 300 elif die < 0.85: | |
| 301 return self._InternalizedString(name) | |
| 302 else: | |
| 303 return self._SlicedString(name) | |
| 304 | |
| 305 def _Symbol(self, name, recursion_budget): | |
| 306 raw_string_name = name + "_1" | |
| 307 result = self._String(raw_string_name, recursion_budget) | |
| 308 return result + self._Variable(name, "Symbol(%s)" % raw_string_name) | |
| 309 | |
| 310 def _Name(self, name, recursion_budget): | |
| 311 if random.random() < 0.2: | |
| 312 return self._Symbol(name, recursion_budget) | |
| 313 return self._String(name, recursion_budget) | |
| 314 | |
| 315 def _JSValue(self, name, recursion_budget): | |
| 316 die = random.random() | |
| 317 raw_name = name + "_1" | |
| 318 if die < 0.33: | |
| 319 result = self._String(raw_name, recursion_budget) | |
| 320 return result + self._Variable(name, "new String(%s)" % raw_name) | |
| 321 elif die < 0.66: | |
| 322 result = self._Boolean(raw_name, recursion_budget) | |
| 323 return result + self._Variable(name, "new Boolean(%s)" % raw_name) | |
| 324 else: | |
| 325 result = self._Number(raw_name, recursion_budget) | |
| 326 return result + self._Variable(name, "new Number(%s)" % raw_name) | |
| 327 | |
| 328 def _RawRandomPropertyName(self): | |
| 329 if random.random() < 0.5: | |
| 330 return random.choice(self.USUAL_SUSPECT_PROPERTIES) | |
| 331 return self._RawRandomString(0, 10) | |
| 332 | |
| 333 def _AddProperties(self, name, result, recursion_budget): | |
| 334 propcount = random.randint(0, 3) | |
| 335 propname = None | |
| 336 for i in range(propcount): | |
| 337 die = random.random() | |
| 338 if die < 0.5: | |
| 339 propname = "%s_prop%d" % (name, i) | |
| 340 result += self._Name(propname, recursion_budget - 1) | |
| 341 else: | |
| 342 propname = "\"%s\"" % self._RawRandomPropertyName() | |
| 343 propvalue_name = "%s_val%d" % (name, i) | |
| 344 result += self._Object(propvalue_name, recursion_budget - 1) | |
| 345 result.append("try { %s[%s] = %s; } catch (e) {}" % | |
| 346 (name, propname, propvalue_name)) | |
| 347 if random.random() < 0.2 and propname: | |
| 348 # Force the object to slow mode. | |
| 349 result.append("delete %s[%s];" % (name, propname)) | |
| 350 | |
| 351 def _RandomElementIndex(self, element_name, result): | |
| 352 if random.random() < 0.5: | |
| 353 return random.randint(-1000, 1000) | |
| 354 result += self._Smi(element_name, 0) | |
| 355 return element_name | |
| 356 | |
| 357 def _AddElements(self, name, result, recursion_budget): | |
| 358 elementcount = random.randint(0, 3) | |
| 359 for i in range(elementcount): | |
| 360 element_name = "%s_idx%d" % (name, i) | |
| 361 index = self._RandomElementIndex(element_name, result) | |
| 362 value_name = "%s_elt%d" % (name, i) | |
| 363 result += self._Object(value_name, recursion_budget - 1) | |
| 364 result.append("try { %s[%s] = %s; } catch(e) {}" % | |
| 365 (name, index, value_name)) | |
| 366 | |
| 367 def _AddAccessors(self, name, result, recursion_budget): | |
| 368 accessorcount = random.randint(0, 3) | |
| 369 for i in range(accessorcount): | |
| 370 propname = self._RawRandomPropertyName() | |
| 371 what = random.choice(["get", "set"]) | |
| 372 function_name = "%s_access%d" % (name, i) | |
| 373 result += self._PlainFunction(function_name, recursion_budget - 1) | |
| 374 result.append("try { Object.defineProperty(%s, \"%s\", {%s: %s}); } " | |
| 375 "catch (e) {}" % (name, propname, what, function_name)) | |
| 376 | |
| 377 def _PlainArray(self, name, recursion_budget): | |
| 378 die = random.random() | |
| 379 if die < 0.5: | |
| 380 literal = random.choice(["[]", "[1, 2]", "[1.5, 2.5]", | |
| 381 "['a', 'b', 1, true]"]) | |
| 382 return self._Variable(name, literal) | |
| 383 else: | |
| 384 new = random.choice(["", "new "]) | |
| 385 length = random.randint(0, 101000) | |
| 386 return self._Variable(name, "%sArray(%d)" % (new, length)) | |
| 387 | |
| 388 def _PlainObject(self, name, recursion_budget): | |
| 389 die = random.random() | |
| 390 if die < 0.67: | |
| 391 literal_propcount = random.randint(0, 3) | |
| 392 properties = [] | |
| 393 result = [] | |
| 394 for i in range(literal_propcount): | |
| 395 propname = self._RawRandomPropertyName() | |
| 396 propvalue_name = "%s_lit%d" % (name, i) | |
| 397 result += self._Object(propvalue_name, recursion_budget - 1) | |
| 398 properties.append("\"%s\": %s" % (propname, propvalue_name)) | |
| 399 return result + self._Variable(name, "{%s}" % ", ".join(properties)) | |
| 400 else: | |
| 401 return self._Variable(name, "new Object()") | |
| 402 | |
| 403 def _JSArray(self, name, recursion_budget): | |
| 404 result = self._PlainArray(name, recursion_budget) | |
| 405 self._AddAccessors(name, result, recursion_budget) | |
| 406 self._AddProperties(name, result, recursion_budget) | |
| 407 self._AddElements(name, result, recursion_budget) | |
| 408 return result | |
| 409 | |
| 410 def _RawRandomBufferLength(self): | |
| 411 if random.random() < 0.2: | |
| 412 return random.choice([0, 1, 8, 0x40000000, 0x80000000]) | |
| 413 return random.randint(0, 1000) | |
| 414 | |
| 415 def _JSArrayBuffer(self, name, recursion_budget): | |
| 416 length = self._RawRandomBufferLength() | |
| 417 return self._Variable(name, "new ArrayBuffer(%d)" % length) | |
| 418 | |
| 419 def _JSDataView(self, name, recursion_budget): | |
| 420 buffer_name = name + "_buffer" | |
| 421 result = self._JSArrayBuffer(buffer_name, recursion_budget) | |
| 422 args = [buffer_name] | |
| 423 die = random.random() | |
| 424 if die < 0.67: | |
| 425 offset = self._RawRandomBufferLength() | |
| 426 args.append("%d" % offset) | |
| 427 if die < 0.33: | |
| 428 length = self._RawRandomBufferLength() | |
| 429 args.append("%d" % length) | |
| 430 result += self._Variable(name, "new DataView(%s)" % ", ".join(args), | |
| 431 fallback="new DataView(new ArrayBuffer(8))") | |
| 432 return result | |
| 433 | |
| 434 def _JSDate(self, name, recursion_budget): | |
| 435 die = random.random() | |
| 436 if die < 0.25: | |
| 437 return self._Variable(name, "new Date()") | |
| 438 elif die < 0.5: | |
| 439 ms_name = name + "_ms" | |
| 440 result = self._Number(ms_name, recursion_budget) | |
| 441 return result + self._Variable(name, "new Date(%s)" % ms_name) | |
| 442 elif die < 0.75: | |
| 443 str_name = name + "_str" | |
| 444 month = random.choice(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", | |
| 445 "Aug", "Sep", "Oct", "Nov", "Dec"]) | |
| 446 day = random.randint(1, 28) | |
| 447 year = random.randint(1900, 2100) | |
| 448 hour = random.randint(0, 23) | |
| 449 minute = random.randint(0, 59) | |
| 450 second = random.randint(0, 59) | |
| 451 str_value = ("\"%s %s, %s %s:%s:%s\"" % | |
| 452 (month, day, year, hour, minute, second)) | |
| 453 result = self._Variable(str_name, str_value) | |
| 454 return result + self._Variable(name, "new Date(%s)" % str_name) | |
| 455 else: | |
| 456 components = tuple(map(lambda x: "%s_%s" % (name, x), | |
| 457 ["y", "m", "d", "h", "min", "s", "ms"])) | |
| 458 return ([j for i in map(self._Int32, components) for j in i] + | |
| 459 self._Variable(name, "new Date(%s)" % ", ".join(components))) | |
| 460 | |
| 461 def _PlainFunction(self, name, recursion_budget): | |
| 462 result_name = "result" | |
| 463 body = ["function() {"] | |
| 464 body += self._Object(result_name, recursion_budget - 1) | |
| 465 body.append("return result;\n}") | |
| 466 return self._Variable(name, "%s" % "\n".join(body)) | |
| 467 | |
| 468 def _JSFunction(self, name, recursion_budget): | |
| 469 result = self._PlainFunction(name, recursion_budget) | |
| 470 self._AddAccessors(name, result, recursion_budget) | |
| 471 self._AddProperties(name, result, recursion_budget) | |
| 472 self._AddElements(name, result, recursion_budget) | |
| 473 return result | |
| 474 | |
| 475 def _JSFunctionProxy(self, name, recursion_budget): | |
| 476 # TODO(jkummerow): Revisit this as the Proxy implementation evolves. | |
| 477 return self._Variable(name, "Proxy.createFunction(%s, function() {})" % | |
| 478 self.PROXY_TRAPS) | |
| 479 | |
| 480 def _JSGeneratorObject(self, name, recursion_budget): | |
| 481 # TODO(jkummerow): Be more creative here? | |
| 482 return self._Variable(name, "(function*() { yield 1; })()") | |
| 483 | |
| 484 def _JSMap(self, name, recursion_budget, weak=""): | |
| 485 result = self._Variable(name, "new %sMap()" % weak) | |
| 486 num_entries = random.randint(0, 3) | |
| 487 for i in range(num_entries): | |
| 488 key_name = "%s_k%d" % (name, i) | |
| 489 value_name = "%s_v%d" % (name, i) | |
| 490 if weak: | |
| 491 result += self._JSObject(key_name, recursion_budget - 1) | |
| 492 else: | |
| 493 result += self._Object(key_name, recursion_budget - 1) | |
| 494 result += self._Object(value_name, recursion_budget - 1) | |
| 495 result.append("%s.set(%s, %s)" % (name, key_name, value_name)) | |
| 496 return result | |
| 497 | |
| 498 def _JSMapIterator(self, name, recursion_budget): | |
| 499 map_name = name + "_map" | |
| 500 result = self._JSMap(map_name, recursion_budget) | |
| 501 iterator_type = random.randint(1, 3) | |
| 502 return (result + self._Variable(name, "%%MapCreateIterator(%s, %d)" % | |
| 503 (map_name, iterator_type))) | |
| 504 | |
| 505 def _JSProxy(self, name, recursion_budget): | |
| 506 # TODO(jkummerow): Revisit this as the Proxy implementation evolves. | |
| 507 return self._Variable(name, "Proxy.create(%s)" % self.PROXY_TRAPS) | |
| 508 | |
| 509 def _JSRegExp(self, name, recursion_budget): | |
| 510 flags = random.choice(["", "g", "i", "m", "gi"]) | |
| 511 string = "a(b|c)*a" # TODO(jkummerow): Be more creative here? | |
| 512 ctor = random.choice(["/%s/%s", "new RegExp(\"%s\", \"%s\")"]) | |
| 513 return self._Variable(name, ctor % (string, flags)) | |
| 514 | |
| 515 def _JSSet(self, name, recursion_budget, weak=""): | |
| 516 result = self._Variable(name, "new %sSet()" % weak) | |
| 517 num_entries = random.randint(0, 3) | |
| 518 for i in range(num_entries): | |
| 519 element_name = "%s_e%d" % (name, i) | |
| 520 if weak: | |
| 521 result += self._JSObject(element_name, recursion_budget - 1) | |
| 522 else: | |
| 523 result += self._Object(element_name, recursion_budget - 1) | |
| 524 result.append("%s.add(%s)" % (name, element_name)) | |
| 525 return result | |
| 526 | |
| 527 def _JSSetIterator(self, name, recursion_budget): | |
| 528 set_name = name + "_set" | |
| 529 result = self._JSSet(set_name, recursion_budget) | |
| 530 iterator_type = random.randint(2, 3) | |
| 531 return (result + self._Variable(name, "%%SetCreateIterator(%s, %d)" % | |
| 532 (set_name, iterator_type))) | |
| 533 | |
| 534 def _JSTypedArray(self, name, recursion_budget): | |
| 535 arraytype = random.choice(["Int8", "Int16", "Int32", "Uint8", "Uint16", | |
| 536 "Uint32", "Float32", "Float64", "Uint8Clamped"]) | |
| 537 ctor_type = random.randint(0, 3) | |
| 538 if ctor_type == 0: | |
| 539 length = random.randint(0, 1000) | |
| 540 return self._Variable(name, "new %sArray(%d)" % (arraytype, length), | |
| 541 fallback="new %sArray(8)" % arraytype) | |
| 542 elif ctor_type == 1: | |
| 543 input_name = name + "_typedarray" | |
| 544 result = self._JSTypedArray(input_name, recursion_budget - 1) | |
| 545 return (result + | |
| 546 self._Variable(name, "new %sArray(%s)" % (arraytype, input_name), | |
| 547 fallback="new %sArray(8)" % arraytype)) | |
| 548 elif ctor_type == 2: | |
| 549 arraylike_name = name + "_arraylike" | |
| 550 result = self._JSObject(arraylike_name, recursion_budget - 1) | |
| 551 length = random.randint(0, 1000) | |
| 552 result.append("try { %s.length = %d; } catch(e) {}" % | |
| 553 (arraylike_name, length)) | |
| 554 return (result + | |
| 555 self._Variable(name, | |
| 556 "new %sArray(%s)" % (arraytype, arraylike_name), | |
| 557 fallback="new %sArray(8)" % arraytype)) | |
| 558 else: | |
| 559 die = random.random() | |
| 560 buffer_name = name + "_buffer" | |
| 561 args = [buffer_name] | |
| 562 result = self._JSArrayBuffer(buffer_name, recursion_budget) | |
| 563 if die < 0.67: | |
| 564 offset_name = name + "_offset" | |
| 565 args.append(offset_name) | |
| 566 result += self._Int32(offset_name) | |
| 567 if die < 0.33: | |
| 568 length_name = name + "_length" | |
| 569 args.append(length_name) | |
| 570 result += self._Int32(length_name) | |
| 571 return (result + | |
| 572 self._Variable(name, | |
| 573 "new %sArray(%s)" % (arraytype, ", ".join(args)), | |
| 574 fallback="new %sArray(8)" % arraytype)) | |
| 575 | |
| 576 def _JSWeakCollection(self, name, recursion_budget): | |
| 577 ctor = random.choice([self._JSMap, self._JSSet]) | |
| 578 return ctor(name, recursion_budget, weak="Weak") | |
| 579 | |
| 580 def _PropertyDetails(self, name, recursion_budget): | |
| 581 # TODO(jkummerow): Be more clever here? | |
| 582 return self._Int32(name) | |
| 583 | |
| 584 def _JSObject(self, name, recursion_budget): | |
| 585 if random.random() < 0.4: | |
| 586 function = random.choice([self._PlainObject, self._PlainArray, | |
| 587 self._PlainFunction]) | |
| 588 else: | |
| 589 function = random.choice([self._JSArrayBuffer, self._JSDataView, | |
| 590 self._JSDate, self._JSFunctionProxy, | |
| 591 self._JSGeneratorObject, self._JSMap, | |
| 592 self._JSMapIterator, self._JSRegExp, | |
| 593 self._JSSet, self._JSSetIterator, | |
| 594 self._JSTypedArray, self._JSValue, | |
| 595 self._JSWeakCollection]) | |
| 596 result = function(name, recursion_budget) | |
| 597 self._AddAccessors(name, result, recursion_budget) | |
| 598 self._AddProperties(name, result, recursion_budget) | |
| 599 self._AddElements(name, result, recursion_budget) | |
| 600 return result | |
| 601 | |
| 602 def _JSReceiver(self, name, recursion_budget): | |
| 603 if random.random() < 0.9: return self._JSObject(name, recursion_budget) | |
| 604 return self._JSProxy(name, recursion_budget) | |
| 605 | |
| 606 def _HeapObject(self, name, recursion_budget): | |
| 607 die = random.random() | |
| 608 if die < 0.9: return self._JSReceiver(name, recursion_budget) | |
| 609 elif die < 0.95: return self._Oddball(name, recursion_budget) | |
| 610 else: return self._Name(name, recursion_budget) | |
| 611 | |
| 612 def _Object(self, name, recursion_budget): | |
| 613 if recursion_budget <= 0: | |
| 614 function = random.choice([self._Oddball, self._Number, self._Name, | |
| 615 self._JSValue, self._JSRegExp]) | |
| 616 return function(name, recursion_budget) | |
| 617 if random.random() < 0.2: | |
| 618 return self._Smi(name, recursion_budget) | |
| 619 return self._HeapObject(name, recursion_budget) | |
| 620 | |
| 621 GENERATORS = { | |
| 622 "Boolean": ["true", _Boolean], | |
| 623 "HeapObject": ["new Object()", _HeapObject], | |
| 624 "Int32": ["32", _Int32], | |
| 625 "JSArray": ["new Array()", _JSArray], | |
| 626 "JSArrayBuffer": ["new ArrayBuffer(8)", _JSArrayBuffer], | |
| 627 "JSDataView": ["new DataView(new ArrayBuffer(8))", _JSDataView], | |
| 628 "JSDate": ["new Date()", _JSDate], | |
| 629 "JSFunction": ["function() {}", _JSFunction], | |
| 630 "JSFunctionProxy": ["Proxy.createFunction({}, function() {})", | |
| 631 _JSFunctionProxy], | |
| 632 "JSGeneratorObject": ["(function*(){ yield 1; })()", _JSGeneratorObject], | |
| 633 "JSMap": ["new Map()", _JSMap], | |
| 634 "JSMapIterator": ["%MapCreateIterator(new Map(), 3)", _JSMapIterator], | |
| 635 "JSObject": ["new Object()", _JSObject], | |
| 636 "JSProxy": ["Proxy.create({})", _JSProxy], | |
| 637 "JSReceiver": ["new Object()", _JSReceiver], | |
| 638 "JSRegExp": ["/ab/g", _JSRegExp], | |
| 639 "JSSet": ["new Set()", _JSSet], | |
| 640 "JSSetIterator": ["%SetCreateIterator(new Set(), 2)", _JSSetIterator], | |
| 641 "JSTypedArray": ["new Int32Array(2)", _JSTypedArray], | |
| 642 "JSValue": ["new String('foo')", _JSValue], | |
| 643 "JSWeakCollection": ["new WeakMap()", _JSWeakCollection], | |
| 644 "Name": ["\"name\"", _Name], | |
| 645 "Number": ["1.5", _Number], | |
| 646 "Object": ["new Object()", _Object], | |
| 647 "PropertyDetails": ["513", _PropertyDetails], | |
| 648 "SeqString": ["\"seqstring\"", _SeqString], | |
| 649 "Smi": ["1", _Smi], | |
| 650 "StrictMode": ["1", _StrictMode], | |
| 651 "String": ["\"foo\"", _String], | |
| 652 "Symbol": ["Symbol(\"symbol\")", _Symbol], | |
| 653 "Uint32": ["32", _Uint32], | |
| 654 } | |
| 193 | 655 | 
| 194 | 656 | 
| 195 class ArgParser(object): | 657 class ArgParser(object): | 
| 196 def __init__(self, regex, ctor): | 658 def __init__(self, regex, ctor): | 
| 197 self.regex = regex | 659 self.regex = regex | 
| 198 self.ArgCtor = ctor | 660 self.ArgCtor = ctor | 
| 199 | 661 | 
| 200 | 662 | 
| 201 class Arg(object): | 663 class Arg(object): | 
| 202 def __init__(self, typename, varname, index): | 664 def __init__(self, typename, varname, index): | 
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 247 | 709 | 
| 248 property_details_parser = ArgParser( | 710 property_details_parser = ArgParser( | 
| 249 re.compile("^\s*CONVERT_PROPERTY_DETAILS_CHECKED\((\w+), (\d+)\)"), | 711 re.compile("^\s*CONVERT_PROPERTY_DETAILS_CHECKED\((\w+), (\d+)\)"), | 
| 250 lambda match: Arg("PropertyDetails", match.group(1), int(match.group(2)))) | 712 lambda match: Arg("PropertyDetails", match.group(1), int(match.group(2)))) | 
| 251 | 713 | 
| 252 arg_parsers = [handle_arg_parser, plain_arg_parser, number_handle_arg_parser, | 714 arg_parsers = [handle_arg_parser, plain_arg_parser, number_handle_arg_parser, | 
| 253 smi_arg_parser, | 715 smi_arg_parser, | 
| 254 double_arg_parser, number_arg_parser, strict_mode_arg_parser, | 716 double_arg_parser, number_arg_parser, strict_mode_arg_parser, | 
| 255 boolean_arg_parser, property_details_parser] | 717 boolean_arg_parser, property_details_parser] | 
| 256 | 718 | 
| 257 | |
| 258 def SetArgsLength(self, match): | 719 def SetArgsLength(self, match): | 
| 259 self.argslength = int(match.group(1)) | 720 self.argslength = int(match.group(1)) | 
| 260 | 721 | 
| 261 def TryParseArg(self, line): | 722 def TryParseArg(self, line): | 
| 262 for parser in Function.arg_parsers: | 723 for parser in Function.arg_parsers: | 
| 263 match = parser.regex.match(line) | 724 match = parser.regex.match(line) | 
| 264 if match: | 725 if match: | 
| 265 arg = parser.ArgCtor(match) | 726 arg = parser.ArgCtor(match) | 
| 266 self.args[arg.index] = arg | 727 self.args[arg.index] = arg | 
| 267 return True | 728 return True | 
| (...skipping 10 matching lines...) Expand all Loading... | |
| 278 if self.args: | 739 if self.args: | 
| 279 argcount = max([self.args[i].index + 1 for i in self.args]) | 740 argcount = max([self.args[i].index + 1 for i in self.args]) | 
| 280 else: | 741 else: | 
| 281 argcount = 0 | 742 argcount = 0 | 
| 282 for i in range(argcount): | 743 for i in range(argcount): | 
| 283 if i > 0: s.append(", ") | 744 if i > 0: s.append(", ") | 
| 284 s.append(self.args[i].type if i in self.args else "<unknown>") | 745 s.append(self.args[i].type if i in self.args else "<unknown>") | 
| 285 s.append(")") | 746 s.append(")") | 
| 286 return "".join(s) | 747 return "".join(s) | 
| 287 | 748 | 
| 749 | |
| 288 # Parses HEADERFILENAME to find out which runtime functions are "inline". | 750 # Parses HEADERFILENAME to find out which runtime functions are "inline". | 
| 289 def FindInlineRuntimeFunctions(): | 751 def FindInlineRuntimeFunctions(): | 
| 290 inline_functions = [] | 752 inline_functions = [] | 
| 291 with open(HEADERFILENAME, "r") as f: | 753 with open(HEADERFILENAME, "r") as f: | 
| 292 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n" | 754 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n" | 
| 293 inline_opt_list = "#define INLINE_OPTIMIZED_FUNCTION_LIST(F) \\\n" | 755 inline_opt_list = "#define INLINE_OPTIMIZED_FUNCTION_LIST(F) \\\n" | 
| 294 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\?") | 756 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\?") | 
| 295 mode = "SEARCHING" | 757 mode = "SEARCHING" | 
| 296 for line in f: | 758 for line in f: | 
| 297 if mode == "ACTIVE": | 759 if mode == "ACTIVE": | 
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 # All good, there's a custom definition. | 846 # All good, there's a custom definition. | 
| 385 pass | 847 pass | 
| 386 elif not i in f.args: | 848 elif not i in f.args: | 
| 387 # No custom definition and no parse result -> give up. | 849 # No custom definition and no parse result -> give up. | 
| 388 decision = unknown_functions | 850 decision = unknown_functions | 
| 389 else: | 851 else: | 
| 390 t = f.args[i].type | 852 t = f.args[i].type | 
| 391 if t in NON_JS_TYPES: | 853 if t in NON_JS_TYPES: | 
| 392 decision = cctest_fuzzable_functions | 854 decision = cctest_fuzzable_functions | 
| 393 else: | 855 else: | 
| 394 assert t in JS_TYPE_GENERATORS, \ | 856 assert Generator.IsTypeSupported(t), \ | 
| 395 ("type generator not found for %s, function: %s" % (t, f)) | 857 ("type generator not found for %s, function: %s" % (t, f)) | 
| 396 decision.append(f) | 858 decision.append(f) | 
| 397 return (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) | 859 return (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) | 
| 398 | 860 | 
| 399 | 861 | 
| 400 def GenerateJSTestcaseForFunction(f): | 862 def _GetKnownGoodArgs(function, generator): | 
| 863 custom_input = CUSTOM_KNOWN_GOOD_INPUT.get(function.name, None) | |
| 864 definitions = [] | |
| 865 argslist = [] | |
| 866 for i in range(function.argslength): | |
| 867 if custom_input and custom_input[i] is not None: | |
| 868 name = "arg%d" % i | |
| 869 definitions.append("var %s = %s;" % (name, custom_input[i])) | |
| 870 else: | |
| 871 arg = function.args[i] | |
| 872 name = arg.name | |
| 873 definitions += generator.RandomVariable(name, arg.type, simple=True) | |
| 874 argslist.append(name) | |
| 875 return (definitions, argslist) | |
| 876 | |
| 877 | |
| 878 def _GenerateTestcase(function, definitions, argslist, throws): | |
| 401 s = ["// Copyright 2014 the V8 project authors. All rights reserved.", | 879 s = ["// Copyright 2014 the V8 project authors. All rights reserved.", | 
| 402 "// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY", | 880 "// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY", | 
| 403 "// Flags: --allow-natives-syntax --harmony"] | 881 "// Flags: --allow-natives-syntax --harmony"] + definitions | 
| 404 call = "%%%s%s(" % (f.inline, f.name) | 882 call = "%%%s%s(%s);" % (function.inline, function.name, ", ".join(argslist)) | 
| 405 custom = CUSTOM_KNOWN_GOOD_INPUT.get(f.name, None) | 883 if throws: | 
| 406 for i in range(f.argslength): | |
| 407 if custom and custom[i] is not None: | |
| 408 (name, value) = ("arg%d" % i, custom[i]) | |
| 409 else: | |
| 410 arg = f.args[i] | |
| 411 (name, value) = (arg.name, JS_TYPE_GENERATORS[arg.type]) | |
| 412 s.append("var %s = %s;" % (name, value)) | |
| 413 if i > 0: call += ", " | |
| 414 call += name | |
| 415 call += ");" | |
| 416 if f.name in THROWS: | |
| 417 s.append("try {") | 884 s.append("try {") | 
| 418 s.append(call); | 885 s.append(call); | 
| 419 s.append("} catch(e) {}") | 886 s.append("} catch(e) {}") | 
| 420 else: | 887 else: | 
| 421 s.append(call) | 888 s.append(call) | 
| 422 testcase = "\n".join(s) | 889 testcase = "\n".join(s) | 
| 423 path = os.path.join(BASEPATH, f.Filename()) | 890 return testcase | 
| 891 | |
| 892 | |
| 893 def GenerateJSTestcaseForFunction(function): | |
| 894 gen = Generator() | |
| 895 (definitions, argslist) = _GetKnownGoodArgs(function, gen) | |
| 896 testcase = _GenerateTestcase(function, definitions, argslist, | |
| 897 function.name in THROWS) | |
| 898 path = os.path.join(BASEPATH, function.Filename()) | |
| 424 with open(path, "w") as f: | 899 with open(path, "w") as f: | 
| 425 f.write("%s\n" % testcase) | 900 f.write("%s\n" % testcase) | 
| 426 | 901 | 
| 902 | |
| 427 def GenerateTestcases(functions): | 903 def GenerateTestcases(functions): | 
| 428 shutil.rmtree(BASEPATH) # Re-generate everything. | 904 shutil.rmtree(BASEPATH) # Re-generate everything. | 
| 429 os.makedirs(BASEPATH) | 905 os.makedirs(BASEPATH) | 
| 430 for f in functions: | 906 for f in functions: | 
| 431 GenerateJSTestcaseForFunction(f) | 907 GenerateJSTestcaseForFunction(f) | 
| 432 | 908 | 
| 433 def PrintUsage(): | 909 | 
| 434 print """Usage: %(this_script)s ACTION | 910 def _SaveFileName(save_path, process_id, save_file_index): | 
| 911 return "%s/fuzz_%d_%d.js" % (save_path, process_id, save_file_index) | |
| 912 | |
| 913 | |
| 914 def RunFuzzer(process_id, options, stop_running): | |
| 915 base_file_name = "/dev/shm/runtime_fuzz_%d" % process_id | |
| 916 test_file_name = "%s.js" % base_file_name | |
| 917 stderr_file_name = "%s.out" % base_file_name | |
| 918 save_file_index = 0 | |
| 919 while os.path.exists(_SaveFileName(options.save_path, process_id, | |
| 920 save_file_index)): | |
| 921 save_file_index += 1 | |
| 922 MAX_SLEEP_TIME = 0.1 | |
| 923 INITIAL_SLEEP_TIME = 0.001 | |
| 924 SLEEP_TIME_FACTOR = 1.5 | |
| 925 | |
| 926 functions = FindRuntimeFunctions() | |
| 927 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ | |
| 928 ClassifyFunctions(functions) | |
| 929 | |
| 930 try: | |
| 931 for i in range(options.num_tests): | |
| 932 if stop_running.is_set(): break | |
| 933 function = random.choice(js_fuzzable_functions) # TODO: others too | |
| 934 if function.argslength == 0: continue | |
| 935 args = [] | |
| 936 definitions = [] | |
| 937 gen = Generator() | |
| 938 for i in range(function.argslength): | |
| 939 arg = function.args[i] | |
| 940 argname = "arg%d%s" % (i, arg.name) | |
| 941 args.append(argname) | |
| 942 definitions += gen.RandomVariable(argname, arg.type, simple=False) | |
| 943 testcase = _GenerateTestcase(function, definitions, args, True) | |
| 944 with open(test_file_name, "w") as f: | |
| 945 f.write("%s\n" % testcase) | |
| 946 with open("/dev/null", "w") as devnull: | |
| 947 with open(stderr_file_name, "w") as stderr: | |
| 948 process = subprocess.Popen( | |
| 949 [options.binary, "--allow-natives-syntax", "--harmony", | |
| 950 "--enable-slow-asserts", test_file_name], | |
| 951 stdout=devnull, stderr=stderr) | |
| 952 end_time = time.time() + options.timeout | |
| 953 timed_out = False | |
| 954 exit_code = None | |
| 955 sleep_time = INITIAL_SLEEP_TIME | |
| 956 while exit_code is None: | |
| 957 if time.time() >= end_time: | |
| 958 # Kill the process and wait for it to exit. | |
| 959 os.kill(process.pid, signal.SIGTERM) | |
| 960 exit_code = process.wait() | |
| 961 timed_out = True | |
| 962 else: | |
| 963 exit_code = process.poll() | |
| 964 time.sleep(sleep_time) | |
| 965 sleep_time = sleep_time * SLEEP_TIME_FACTOR | |
| 966 if sleep_time > MAX_SLEEP_TIME: | |
| 967 sleep_time = MAX_SLEEP_TIME | |
| 968 if exit_code != 0 and not timed_out: | |
| 969 oom = False | |
| 970 with open(stderr_file_name, "r") as stderr: | |
| 971 for line in stderr: | |
| 972 if line.strip() == "# Allocation failed - process out of memory": | |
| 973 oom = True | |
| 974 break | |
| 975 if oom: continue | |
| 976 save_name = _SaveFileName(options.save_path, process_id, | |
| 977 save_file_index) | |
| 978 shutil.copyfile(test_file_name, save_name) | |
| 979 save_file_index += 1 | |
| 980 except KeyboardInterrupt: | |
| 981 stop_running.set() | |
| 982 except Exception, e: | |
| 983 print e | |
| 984 finally: | |
| 985 os.remove(test_file_name) | |
| 986 os.remove(stderr_file_name) | |
| 987 | |
| 988 | |
| 989 def BuildOptionParser(): | |
| 990 usage = """Usage: %%prog [options] ACTION | |
| 435 | 991 | 
| 436 where ACTION can be: | 992 where ACTION can be: | 
| 437 | 993 | 
| 438 info Print diagnostic info. | 994 info Print diagnostic info. | 
| 439 check Check that runtime functions can be parsed as expected, and that | 995 check Check that runtime functions can be parsed as expected, and that | 
| 440 test cases exist. | 996 test cases exist. | 
| 441 generate Parse source code for runtime functions, and auto-generate | 997 generate Parse source code for runtime functions, and auto-generate | 
| 442 test cases for them. Warning: this will nuke and re-create | 998 test cases for them. Warning: this will nuke and re-create | 
| 443 %(path)s. | 999 %(path)s. | 
| 444 """ % {"path": os.path.relpath(BASEPATH), "this_script": THIS_SCRIPT} | 1000 fuzz Generate fuzz tests, run them, save those that crashed (see options). | 
| 1001 """ % {"path": os.path.relpath(BASEPATH)} | |
| 445 | 1002 | 
| 446 if __name__ == "__main__": | 1003 o = optparse.OptionParser(usage=usage) | 
| 447 if len(sys.argv) != 2: | 1004 o.add_option("--binary", default="out/x64.debug/d8", | 
| 448 PrintUsage() | 1005 help="d8 binary used for running fuzz tests (default: %default)") | 
| 449 sys.exit(1) | 1006 o.add_option("-n", "--num-tests", default=1000, type="int", | 
| 450 action = sys.argv[1] | 1007 help="Number of fuzz tests to generate per worker process" | 
| 451 if action in ["-h", "--help", "help"]: | 1008 " (default: %default)") | 
| 452 PrintUsage() | 1009 o.add_option("--save-path", default="~/runtime_fuzz_output", | 
| 453 sys.exit(0) | 1010 help="Path to directory where failing tests will be stored" | 
| 1011 " (default: %default)") | |
| 1012 o.add_option("--timeout", default=20, type="int", | |
| 1013 help="Timeout for each fuzz test (in seconds, default:" | |
| 1014 "%default)") | |
| 1015 return o | |
| 1016 | |
| 1017 | |
| 1018 def Main(): | |
| 1019 parser = BuildOptionParser() | |
| 1020 (options, args) = parser.parse_args() | |
| 1021 options.save_path = os.path.expanduser(options.save_path) | |
| 1022 | |
| 1023 if len(args) != 1 or args[0] == "help": | |
| 1024 parser.print_help() | |
| 1025 return 1 | |
| 1026 action = args[0] | |
| 454 | 1027 | 
| 455 functions = FindRuntimeFunctions() | 1028 functions = FindRuntimeFunctions() | 
| 456 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ | 1029 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \ | 
| 457 ClassifyFunctions(functions) | 1030 ClassifyFunctions(functions) | 
| 458 | 1031 | 
| 1032 if action == "test": | |
| 1033 gen = Generator() | |
| 1034 vartype = "JSTypedArray" | |
| 1035 print("simple: %s" % gen.RandomVariable("x", vartype, True)) | |
| 1036 for i in range(10): | |
| 1037 print("----") | |
| 1038 print("%s" % "\n".join(gen.RandomVariable("x", vartype, False))) | |
| 1039 return 0 | |
| 1040 | |
| 459 if action == "info": | 1041 if action == "info": | 
| 460 print("%d functions total; js_fuzzable_functions: %d, " | 1042 print("%d functions total; js_fuzzable_functions: %d, " | 
| 461 "cctest_fuzzable_functions: %d, unknown_functions: %d" | 1043 "cctest_fuzzable_functions: %d, unknown_functions: %d" | 
| 462 % (len(functions), len(js_fuzzable_functions), | 1044 % (len(functions), len(js_fuzzable_functions), | 
| 463 len(cctest_fuzzable_functions), len(unknown_functions))) | 1045 len(cctest_fuzzable_functions), len(unknown_functions))) | 
| 464 print("unknown functions:") | 1046 print("unknown functions:") | 
| 465 for f in unknown_functions: | 1047 for f in unknown_functions: | 
| 466 print(f) | 1048 print(f) | 
| 467 sys.exit(0) | 1049 return 0 | 
| 468 | 1050 | 
| 469 if action == "check": | 1051 if action == "check": | 
| 470 error = False | 1052 errors = 0 | 
| 1053 | |
| 471 def CheckCount(actual, expected, description): | 1054 def CheckCount(actual, expected, description): | 
| 472 global error | |
| 473 if len(actual) != expected: | 1055 if len(actual) != expected: | 
| 474 print("Expected to detect %d %s, but found %d." % ( | 1056 print("Expected to detect %d %s, but found %d." % ( | 
| 475 expected, description, len(actual))) | 1057 expected, description, len(actual))) | 
| 476 print("If this change is intentional, please update the expectations" | 1058 print("If this change is intentional, please update the expectations" | 
| 477 " at the top of %s." % THIS_SCRIPT) | 1059 " at the top of %s." % THIS_SCRIPT) | 
| 478 error = True | 1060 return 1 | 
| 479 CheckCount(functions, EXPECTED_FUNCTION_COUNT, "functions in total") | 1061 return 0 | 
| 480 CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT, | 1062 | 
| 481 "JavaScript-fuzzable functions") | 1063 errors += CheckCount(functions, EXPECTED_FUNCTION_COUNT, | 
| 482 CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT, | 1064 "functions in total") | 
| 483 "cctest-fuzzable functions") | 1065 errors += CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT, | 
| 484 CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT, | 1066 "JavaScript-fuzzable functions") | 
| 485 "functions with incomplete type information") | 1067 errors += CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT, | 
| 1068 "cctest-fuzzable functions") | |
| 1069 errors += CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT, | |
| 1070 "functions with incomplete type information") | |
| 486 | 1071 | 
| 487 def CheckTestcasesExisting(functions): | 1072 def CheckTestcasesExisting(functions): | 
| 488 global error | 1073 errors = 0 | 
| 489 for f in functions: | 1074 for f in functions: | 
| 490 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())): | 1075 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())): | 
| 491 print("Missing testcase for %s, please run '%s generate'" % | 1076 print("Missing testcase for %s, please run '%s generate'" % | 
| 492 (f.name, THIS_SCRIPT)) | 1077 (f.name, THIS_SCRIPT)) | 
| 493 error = True | 1078 errors += 1 | 
| 494 files = filter(lambda filename: not filename.startswith("."), | 1079 files = filter(lambda filename: not filename.startswith("."), | 
| 495 os.listdir(BASEPATH)) | 1080 os.listdir(BASEPATH)) | 
| 496 if (len(files) != len(functions)): | 1081 if (len(files) != len(functions)): | 
| 497 unexpected_files = set(files) - set([f.Filename() for f in functions]) | 1082 unexpected_files = set(files) - set([f.Filename() for f in functions]) | 
| 498 for f in unexpected_files: | 1083 for f in unexpected_files: | 
| 499 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f)) | 1084 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f)) | 
| 500 error = True | 1085 errors += 1 | 
| 501 print("Run '%s generate' to automatically clean these up." | 1086 print("Run '%s generate' to automatically clean these up." | 
| 502 % THIS_SCRIPT) | 1087 % THIS_SCRIPT) | 
| 503 CheckTestcasesExisting(js_fuzzable_functions) | 1088 return errors | 
| 504 | 1089 | 
| 505 if error: | 1090 errors += CheckTestcasesExisting(js_fuzzable_functions) | 
| 506 sys.exit(1) | 1091 | 
| 1092 if errors > 0: | |
| 1093 return 1 | |
| 507 print("Generated runtime tests: all good.") | 1094 print("Generated runtime tests: all good.") | 
| 508 sys.exit(0) | 1095 return 0 | 
| 509 | 1096 | 
| 510 if action == "generate": | 1097 if action == "generate": | 
| 511 GenerateTestcases(js_fuzzable_functions) | 1098 GenerateTestcases(js_fuzzable_functions) | 
| 512 sys.exit(0) | 1099 return 0 | 
| 1100 | |
| 1101 if action == "fuzz": | |
| 1102 processes = [] | |
| 1103 if not os.path.isdir(options.save_path): | |
| 1104 os.makedirs(options.save_path) | |
| 1105 stop_running = multiprocessing.Event() | |
| 1106 for i in range(multiprocessing.cpu_count()): | |
| 1107 args = (i, options, stop_running) | |
| 1108 p = multiprocessing.Process(target=RunFuzzer, args=args) | |
| 1109 p.start() | |
| 1110 processes.append(p) | |
| 1111 try: | |
| 1112 for i in range(len(processes)): | |
| 1113 processes[i].join() | |
| 1114 except KeyboardInterrupt: | |
| 1115 stop_running.set() | |
| 1116 for i in range(len(processes)): | |
| 1117 processes[i].join() | |
| 1118 return 0 | |
| 1119 | |
| 1120 if __name__ == "__main__": | |
| 1121 sys.exit(Main()) | |
| OLD | NEW |