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