Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Side by Side Diff: tools/generate-runtime-tests.py

Issue 288443005: Add randomized test generation capability to tools/generate-runtime-tests.py (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: addressed comments Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698