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

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: cleanup 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"]
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
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
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
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())
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