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

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

Issue 548383002: Delete generated runtime tests (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 3 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 | « tools/check-name-clashes.py ('k') | tools/presubmit.py » ('j') | 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 js2c 6 import js2c
8 import multiprocessing
9 import optparse
10 import os 7 import os
11 import random
12 import re 8 import re
13 import shutil
14 import signal
15 import string
16 import subprocess
17 import sys 9 import sys
18 import time
19 10
20 FILENAME = "src/runtime.cc" 11 FILENAME = "src/runtime.cc"
21 HEADERFILENAME = "src/runtime.h"
22 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)") 12 FUNCTION = re.compile("^RUNTIME_FUNCTION\(Runtime_(\w+)")
23 ARGSLENGTH = re.compile(".*DCHECK\(.*args\.length\(\) == (\d+)\);")
24 FUNCTIONEND = "}\n" 13 FUNCTIONEND = "}\n"
25 MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$") 14 MACRO = re.compile(r"^#define ([^ ]+)\(([^)]*)\) *([^\\]*)\\?\n$")
26 FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]") 15 FIRST_WORD = re.compile("^\s*(.*?)[\s({\[]")
27 16
28 WORKSPACE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
29 BASEPATH = os.path.join(WORKSPACE, "test", "mjsunit", "runtime-gen")
30 THIS_SCRIPT = os.path.relpath(sys.argv[0])
31
32 # Expand these macros, they define further runtime functions. 17 # Expand these macros, they define further runtime functions.
33 EXPAND_MACROS = [ 18 EXPAND_MACROS = [
34 "BUFFER_VIEW_GETTER", 19 "BUFFER_VIEW_GETTER",
35 "DATA_VIEW_GETTER", 20 "DATA_VIEW_GETTER",
36 "DATA_VIEW_SETTER", 21 "DATA_VIEW_SETTER",
22 "ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION",
23 "FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION",
37 "RUNTIME_UNARY_MATH", 24 "RUNTIME_UNARY_MATH",
25 "TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION",
38 ] 26 ]
39 # TODO(jkummerow): We could also whitelist the following macros, but the
40 # functions they define are so trivial that it's unclear how much benefit
41 # that would provide:
42 # ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION
43 # FIXED_TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
44 # TYPED_ARRAYS_CHECK_RUNTIME_FUNCTION
45
46 # Counts of functions in each detection state. These are used to assert
47 # that the parser doesn't bit-rot. Change the values as needed when you add,
48 # remove or change runtime functions, but make sure we don't lose our ability
49 # to parse them!
50 EXPECTED_FUNCTION_COUNT = 431
51 EXPECTED_FUZZABLE_COUNT = 330
52 EXPECTED_CCTEST_COUNT = 7
53 EXPECTED_UNKNOWN_COUNT = 17
54 EXPECTED_BUILTINS_COUNT = 806
55
56
57 # Don't call these at all.
58 BLACKLISTED = [
59 "Abort", # Kills the process.
60 "AbortJS", # Kills the process.
61 "CompileForOnStackReplacement", # Riddled with DCHECK.
62 "IS_VAR", # Not implemented in the runtime.
63 "ListNatives", # Not available in Release mode.
64 "SetAllocationTimeout", # Too slow for fuzzing.
65 "SystemBreak", # Kills (int3) the process.
66
67 # These are weird. They violate some invariants when called after
68 # bootstrapping.
69 "DisableAccessChecks",
70 "EnableAccessChecks",
71
72 # The current LiveEdit implementation relies on and messes with internals
73 # in ways that makes it fundamentally unfuzzable :-(
74 "DebugGetLoadedScripts",
75 "DebugSetScriptSource",
76 "LiveEditFindSharedFunctionInfosForScript",
77 "LiveEditFunctionSourceUpdated",
78 "LiveEditGatherCompileInfo",
79 "LiveEditPatchFunctionPositions",
80 "LiveEditReplaceFunctionCode",
81 "LiveEditReplaceRefToNestedFunction",
82 "LiveEditReplaceScript",
83 "LiveEditRestartFrame",
84 "SetScriptBreakPoint",
85
86 # TODO(jkummerow): Fix these and un-blacklist them!
87 "CreateDateTimeFormat",
88 "CreateNumberFormat",
89
90 # TODO(danno): Fix these internal function that are only callable form stubs
91 # and un-blacklist them!
92 "NumberToString",
93 "RxegExpConstructResult",
94 "RegExpExec",
95 "StringAdd",
96 "SubString",
97 "StringCompare",
98 "StringCharCodeAt",
99 "GetFromCache",
100
101 # Compilation
102 "CompileUnoptimized",
103 "CompileOptimized",
104 "TryInstallOptimizedCode",
105 "NotifyDeoptimized",
106 "NotifyStubFailure",
107
108 # Utilities
109 "AllocateInNewSpace",
110 "AllocateInTargetSpace",
111 "AllocateHeapNumber",
112 "LoadMutableDouble",
113 "NumberToSmi",
114 "NumberToStringSkipCache",
115
116 "FunctionBindArguments",
117 "NewSloppyArguments",
118 "NewStrictArguments",
119
120 # Harmony
121 "CreateJSGeneratorObject",
122 "SuspendJSGeneratorObject",
123 "ResumeJSGeneratorObject",
124 "ThrowGeneratorStateError",
125
126 # Arrays
127 "ArrayConstructor",
128 "InternalArrayConstructor",
129 "NormalizeElements",
130
131 # Literals
132 "MaterializeRegExpLiteral",
133 "CreateObjectLiteral",
134 "CreateArrayLiteral",
135 "CreateArrayLiteralStubBailout",
136
137 # Statements
138 "NewClosure",
139 "NewClosureFromStubFailure",
140 "NewObject",
141 "NewObjectWithAllocationSite",
142 "FinalizeInstanceSize",
143 "Throw",
144 "ReThrow",
145 "ThrowReferenceError",
146 "ThrowNotDateError",
147 "StackGuard",
148 "Interrupt",
149 "PromoteScheduledException",
150
151 # Contexts
152 "NewGlobalContext",
153 "NewFunctionContext",
154 "PushWithContext",
155 "PushCatchContext",
156 "PushBlockContext",
157 "PushModuleContext",
158 "DeleteLookupSlot",
159 "LoadLookupSlot",
160 "LoadLookupSlotNoReferenceError",
161 "StoreLookupSlot",
162
163 # Declarations
164 "DeclareGlobals",
165 "DeclareModules",
166 "DeclareContextSlot",
167 "InitializeConstGlobal",
168 "InitializeConstContextSlot",
169
170 # Eval
171 "ResolvePossiblyDirectEval",
172
173 # Maths
174 "MathPowSlow",
175 "MathPowRT",
176
177 # Internal
178 "InternalSetPrototype",
179 ]
180
181
182 # These will always throw.
183 THROWS = [
184 "CheckExecutionState", # Needs to hit a break point.
185 "CheckIsBootstrapping", # Needs to be bootstrapping.
186 "DebugEvaluate", # Needs to hit a break point.
187 "DebugEvaluateGlobal", # Needs to hit a break point.
188 "DebugIndexedInterceptorElementValue", # Needs an indexed interceptor.
189 "DebugNamedInterceptorPropertyValue", # Needs a named interceptor.
190 "DebugSetScriptSource", # Checks compilation state of script.
191 "GetAllScopesDetails", # Needs to hit a break point.
192 "GetFrameCount", # Needs to hit a break point.
193 "GetFrameDetails", # Needs to hit a break point.
194 "GetRootNaN", # Needs to be bootstrapping.
195 "GetScopeCount", # Needs to hit a break point.
196 "GetScopeDetails", # Needs to hit a break point.
197 "GetStepInPositions", # Needs to hit a break point.
198 "GetTemplateField", # Needs a {Function,Object}TemplateInfo.
199 "GetThreadCount", # Needs to hit a break point.
200 "GetThreadDetails", # Needs to hit a break point.
201 "IsAccessAllowedForObserver", # Needs access-check-required object.
202 "UnblockConcurrentRecompilation" # Needs --block-concurrent-recompilation.
203 ]
204
205
206 # Definitions used in CUSTOM_KNOWN_GOOD_INPUT below.
207 _BREAK_ITERATOR = (
208 "%GetImplFromInitializedIntlObject(new Intl.v8BreakIterator())")
209 _COLLATOR = "%GetImplFromInitializedIntlObject(new Intl.Collator('en-US'))"
210 _DATETIME_FORMAT = (
211 "%GetImplFromInitializedIntlObject(new Intl.DateTimeFormat('en-US'))")
212 _NUMBER_FORMAT = (
213 "%GetImplFromInitializedIntlObject(new Intl.NumberFormat('en-US'))")
214
215
216 # Custom definitions for function input that does not throw.
217 # Format: "FunctionName": ["arg0", "arg1", ..., argslength].
218 # None means "fall back to autodetected value".
219 CUSTOM_KNOWN_GOOD_INPUT = {
220 "AddNamedProperty": [None, "\"bla\"", None, None, None],
221 "AddPropertyForTemplate": [None, 10, None, None, None],
222 "Apply": ["function() {}", None, None, None, None, None],
223 "ArrayBufferSliceImpl": [None, None, 0, None],
224 "ArrayConcat": ["[1, 'a']", None],
225 "BreakIteratorAdoptText": [_BREAK_ITERATOR, None, None],
226 "BreakIteratorBreakType": [_BREAK_ITERATOR, None],
227 "BreakIteratorCurrent": [_BREAK_ITERATOR, None],
228 "BreakIteratorFirst": [_BREAK_ITERATOR, None],
229 "BreakIteratorNext": [_BREAK_ITERATOR, None],
230 "CompileString": [None, "false", None],
231 "CreateBreakIterator": ["'en-US'", "{type: 'string'}", None, None],
232 "CreateJSFunctionProxy": [None, "function() {}", None, None, None],
233 "CreatePrivateSymbol": ["\"foo\"", None],
234 "CreatePrivateOwnSymbol": ["\"foo\"", None],
235 "CreateSymbol": ["\"foo\"", None],
236 "DateParseString": [None, "new Array(8)", None],
237 "DefineAccessorPropertyUnchecked": [None, None, "function() {}",
238 "function() {}", 2, None],
239 "FunctionBindArguments": [None, None, "undefined", None, None],
240 "GetBreakLocations": [None, 0, None],
241 "GetDefaultReceiver": ["function() {}", None],
242 "GetImplFromInitializedIntlObject": ["new Intl.NumberFormat('en-US')", None],
243 "InternalCompare": [_COLLATOR, None, None, None],
244 "InternalDateFormat": [_DATETIME_FORMAT, None, None],
245 "InternalDateParse": [_DATETIME_FORMAT, None, None],
246 "InternalNumberFormat": [_NUMBER_FORMAT, None, None],
247 "InternalNumberParse": [_NUMBER_FORMAT, None, None],
248 "IsSloppyModeFunction": ["function() {}", None],
249 "LoadMutableDouble": ["{foo: 1.2}", None, None],
250 "NewObjectFromBound": ["(function() {}).bind({})", None],
251 "NumberToRadixString": [None, "2", None],
252 "ParseJson": ["\"{}\"", 1],
253 "RegExpExecMultiple": [None, None, "['a']", "['a']", None],
254 "DefineApiAccessorProperty": [None, None, "undefined", "undefined", None, None ],
255 "SetIteratorInitialize": [None, None, "2", None],
256 "SetDebugEventListener": ["undefined", None, None],
257 "SetFunctionBreakPoint": [None, 218, None, None],
258 "StringBuilderConcat": ["[1, 2, 3]", 3, None, None],
259 "StringBuilderJoin": ["['a', 'b']", 4, None, None],
260 "StringMatch": [None, None, "['a', 'b']", None],
261 "StringNormalize": [None, 2, None],
262 "StringReplaceGlobalRegExpWithString": [None, None, None, "['a']", None],
263 "TypedArrayInitialize": [None, 6, "new ArrayBuffer(8)", None, 4, None],
264 "TypedArrayInitializeFromArrayLike": [None, 6, None, None, None],
265 "TypedArraySetFastCases": [None, None, "0", None],
266 "FunctionIsArrow": ["() => null", None],
267 }
268
269
270 # Types of arguments that cannot be generated in a JavaScript testcase.
271 NON_JS_TYPES = [
272 "Code", "Context", "FixedArray", "FunctionTemplateInfo",
273 "JSFunctionResultCache", "JSMessageObject", "Map", "ScopeInfo",
274 "SharedFunctionInfo"]
275
276
277 class Generator(object):
278
279 def RandomVariable(self, varname, vartype, simple):
280 if simple:
281 return self._Variable(varname, self.GENERATORS[vartype][0])
282 return self.GENERATORS[vartype][1](self, varname,
283 self.DEFAULT_RECURSION_BUDGET)
284
285 @staticmethod
286 def IsTypeSupported(typename):
287 return typename in Generator.GENERATORS
288
289 USUAL_SUSPECT_PROPERTIES = ["size", "length", "byteLength", "__proto__",
290 "prototype", "0", "1", "-1"]
291 DEFAULT_RECURSION_BUDGET = 2
292 PROXY_TRAPS = """{
293 getOwnPropertyDescriptor: function(name) {
294 return {value: function() {}, configurable: true, writable: true,
295 enumerable: true};
296 },
297 getPropertyDescriptor: function(name) {
298 return {value: function() {}, configurable: true, writable: true,
299 enumerable: true};
300 },
301 getOwnPropertyNames: function() { return []; },
302 getPropertyNames: function() { return []; },
303 defineProperty: function(name, descriptor) {},
304 delete: function(name) { return true; },
305 fix: function() {}
306 }"""
307
308 def _Variable(self, name, value, fallback=None):
309 args = { "name": name, "value": value, "fallback": fallback }
310 if fallback:
311 wrapper = "try { %%s } catch(e) { var %(name)s = %(fallback)s; }" % args
312 else:
313 wrapper = "%s"
314 return [wrapper % ("var %(name)s = %(value)s;" % args)]
315
316 def _Boolean(self, name, recursion_budget):
317 return self._Variable(name, random.choice(["true", "false"]))
318
319 def _Oddball(self, name, recursion_budget):
320 return self._Variable(name,
321 random.choice(["true", "false", "undefined", "null"]))
322
323 def _StrictMode(self, name, recursion_budget):
324 return self._Variable(name, random.choice([0, 1]))
325
326 def _Int32(self, name, recursion_budget=0):
327 die = random.random()
328 if die < 0.5:
329 value = random.choice([-3, -1, 0, 1, 2, 10, 515, 0x3fffffff, 0x7fffffff,
330 0x40000000, -0x40000000, -0x80000000])
331 elif die < 0.75:
332 value = random.randint(-1000, 1000)
333 else:
334 value = random.randint(-0x80000000, 0x7fffffff)
335 return self._Variable(name, value)
336
337 def _Uint32(self, name, recursion_budget=0):
338 die = random.random()
339 if die < 0.5:
340 value = random.choice([0, 1, 2, 3, 4, 8, 0x3fffffff, 0x40000000,
341 0x7fffffff, 0xffffffff])
342 elif die < 0.75:
343 value = random.randint(0, 1000)
344 else:
345 value = random.randint(0, 0xffffffff)
346 return self._Variable(name, value)
347
348 def _Smi(self, name, recursion_budget):
349 die = random.random()
350 if die < 0.5:
351 value = random.choice([-5, -1, 0, 1, 2, 3, 0x3fffffff, -0x40000000])
352 elif die < 0.75:
353 value = random.randint(-1000, 1000)
354 else:
355 value = random.randint(-0x40000000, 0x3fffffff)
356 return self._Variable(name, value)
357
358 def _Number(self, name, recursion_budget):
359 die = random.random()
360 if die < 0.5:
361 return self._Smi(name, recursion_budget)
362 elif die < 0.6:
363 value = random.choice(["Infinity", "-Infinity", "NaN", "-0",
364 "1.7976931348623157e+308", # Max value.
365 "2.2250738585072014e-308", # Min value.
366 "4.9406564584124654e-324"]) # Min subnormal.
367 else:
368 value = random.lognormvariate(0, 15)
369 return self._Variable(name, value)
370
371 def _RawRandomString(self, minlength=0, maxlength=100,
372 alphabet=string.ascii_letters):
373 length = random.randint(minlength, maxlength)
374 result = ""
375 for i in xrange(length):
376 result += random.choice(alphabet)
377 return result
378
379 def _SeqString(self, name, recursion_budget):
380 s1 = self._RawRandomString(1, 5)
381 s2 = self._RawRandomString(1, 5)
382 # 'foo' + 'bar'
383 return self._Variable(name, "\"%s\" + \"%s\"" % (s1, s2))
384
385 def _SeqTwoByteString(self, name):
386 s1 = self._RawRandomString(1, 5)
387 s2 = self._RawRandomString(1, 5)
388 # 'foo' + unicode + 'bar'
389 return self._Variable(name, "\"%s\" + \"\\2082\" + \"%s\"" % (s1, s2))
390
391 def _SlicedString(self, name):
392 s = self._RawRandomString(20, 30)
393 # 'ffoo12345678901234567890'.substr(1)
394 return self._Variable(name, "\"%s\".substr(1)" % s)
395
396 def _ConsString(self, name):
397 s1 = self._RawRandomString(8, 15)
398 s2 = self._RawRandomString(8, 15)
399 # 'foo12345' + (function() { return 'bar12345';})()
400 return self._Variable(name,
401 "\"%s\" + (function() { return \"%s\";})()" % (s1, s2))
402
403 def _InternalizedString(self, name):
404 return self._Variable(name, "\"%s\"" % self._RawRandomString(0, 20))
405
406 def _String(self, name, recursion_budget):
407 die = random.random()
408 if die < 0.5:
409 string = random.choice(self.USUAL_SUSPECT_PROPERTIES)
410 return self._Variable(name, "\"%s\"" % string)
411 elif die < 0.6:
412 number_name = name + "_number"
413 result = self._Number(number_name, recursion_budget)
414 return result + self._Variable(name, "\"\" + %s" % number_name)
415 elif die < 0.7:
416 return self._SeqString(name, recursion_budget)
417 elif die < 0.8:
418 return self._ConsString(name)
419 elif die < 0.9:
420 return self._InternalizedString(name)
421 else:
422 return self._SlicedString(name)
423
424 def _Symbol(self, name, recursion_budget):
425 raw_string_name = name + "_1"
426 result = self._String(raw_string_name, recursion_budget)
427 return result + self._Variable(name, "Symbol(%s)" % raw_string_name)
428
429 def _Name(self, name, recursion_budget):
430 if random.random() < 0.2:
431 return self._Symbol(name, recursion_budget)
432 return self._String(name, recursion_budget)
433
434 def _JSValue(self, name, recursion_budget):
435 die = random.random()
436 raw_name = name + "_1"
437 if die < 0.33:
438 result = self._String(raw_name, recursion_budget)
439 return result + self._Variable(name, "new String(%s)" % raw_name)
440 elif die < 0.66:
441 result = self._Boolean(raw_name, recursion_budget)
442 return result + self._Variable(name, "new Boolean(%s)" % raw_name)
443 else:
444 result = self._Number(raw_name, recursion_budget)
445 return result + self._Variable(name, "new Number(%s)" % raw_name)
446
447 def _RawRandomPropertyName(self):
448 if random.random() < 0.5:
449 return random.choice(self.USUAL_SUSPECT_PROPERTIES)
450 return self._RawRandomString(0, 10)
451
452 def _AddProperties(self, name, result, recursion_budget):
453 propcount = random.randint(0, 3)
454 propname = None
455 for i in range(propcount):
456 die = random.random()
457 if die < 0.5:
458 propname = "%s_prop%d" % (name, i)
459 result += self._Name(propname, recursion_budget - 1)
460 else:
461 propname = "\"%s\"" % self._RawRandomPropertyName()
462 propvalue_name = "%s_val%d" % (name, i)
463 result += self._Object(propvalue_name, recursion_budget - 1)
464 result.append("try { %s[%s] = %s; } catch (e) {}" %
465 (name, propname, propvalue_name))
466 if random.random() < 0.2 and propname:
467 # Force the object to slow mode.
468 result.append("delete %s[%s];" % (name, propname))
469
470 def _RandomElementIndex(self, element_name, result):
471 if random.random() < 0.5:
472 return random.randint(-1000, 1000)
473 result += self._Smi(element_name, 0)
474 return element_name
475
476 def _AddElements(self, name, result, recursion_budget):
477 elementcount = random.randint(0, 3)
478 for i in range(elementcount):
479 element_name = "%s_idx%d" % (name, i)
480 index = self._RandomElementIndex(element_name, result)
481 value_name = "%s_elt%d" % (name, i)
482 result += self._Object(value_name, recursion_budget - 1)
483 result.append("try { %s[%s] = %s; } catch(e) {}" %
484 (name, index, value_name))
485
486 def _AddAccessors(self, name, result, recursion_budget):
487 accessorcount = random.randint(0, 3)
488 for i in range(accessorcount):
489 propname = self._RawRandomPropertyName()
490 what = random.choice(["get", "set"])
491 function_name = "%s_access%d" % (name, i)
492 result += self._PlainFunction(function_name, recursion_budget - 1)
493 result.append("try { Object.defineProperty(%s, \"%s\", {%s: %s}); } "
494 "catch (e) {}" % (name, propname, what, function_name))
495
496 def _PlainArray(self, name, recursion_budget):
497 die = random.random()
498 if die < 0.5:
499 literal = random.choice(["[]", "[1, 2]", "[1.5, 2.5]",
500 "['a', 'b', 1, true]"])
501 return self._Variable(name, literal)
502 else:
503 new = random.choice(["", "new "])
504 length = random.randint(0, 101000)
505 return self._Variable(name, "%sArray(%d)" % (new, length))
506
507 def _PlainObject(self, name, recursion_budget):
508 die = random.random()
509 if die < 0.67:
510 literal_propcount = random.randint(0, 3)
511 properties = []
512 result = []
513 for i in range(literal_propcount):
514 propname = self._RawRandomPropertyName()
515 propvalue_name = "%s_lit%d" % (name, i)
516 result += self._Object(propvalue_name, recursion_budget - 1)
517 properties.append("\"%s\": %s" % (propname, propvalue_name))
518 return result + self._Variable(name, "{%s}" % ", ".join(properties))
519 else:
520 return self._Variable(name, "new Object()")
521
522 def _JSArray(self, name, recursion_budget):
523 result = self._PlainArray(name, recursion_budget)
524 self._AddAccessors(name, result, recursion_budget)
525 self._AddProperties(name, result, recursion_budget)
526 self._AddElements(name, result, recursion_budget)
527 return result
528
529 def _RawRandomBufferLength(self):
530 if random.random() < 0.2:
531 return random.choice([0, 1, 8, 0x40000000, 0x80000000])
532 return random.randint(0, 1000)
533
534 def _JSArrayBuffer(self, name, recursion_budget):
535 length = self._RawRandomBufferLength()
536 return self._Variable(name, "new ArrayBuffer(%d)" % length)
537
538 def _JSDataView(self, name, recursion_budget):
539 buffer_name = name + "_buffer"
540 result = self._JSArrayBuffer(buffer_name, recursion_budget)
541 args = [buffer_name]
542 die = random.random()
543 if die < 0.67:
544 offset = self._RawRandomBufferLength()
545 args.append("%d" % offset)
546 if die < 0.33:
547 length = self._RawRandomBufferLength()
548 args.append("%d" % length)
549 result += self._Variable(name, "new DataView(%s)" % ", ".join(args),
550 fallback="new DataView(new ArrayBuffer(8))")
551 return result
552
553 def _JSDate(self, name, recursion_budget):
554 die = random.random()
555 if die < 0.25:
556 return self._Variable(name, "new Date()")
557 elif die < 0.5:
558 ms_name = name + "_ms"
559 result = self._Number(ms_name, recursion_budget)
560 return result + self._Variable(name, "new Date(%s)" % ms_name)
561 elif die < 0.75:
562 str_name = name + "_str"
563 month = random.choice(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
564 "Aug", "Sep", "Oct", "Nov", "Dec"])
565 day = random.randint(1, 28)
566 year = random.randint(1900, 2100)
567 hour = random.randint(0, 23)
568 minute = random.randint(0, 59)
569 second = random.randint(0, 59)
570 str_value = ("\"%s %s, %s %s:%s:%s\"" %
571 (month, day, year, hour, minute, second))
572 result = self._Variable(str_name, str_value)
573 return result + self._Variable(name, "new Date(%s)" % str_name)
574 else:
575 components = tuple(map(lambda x: "%s_%s" % (name, x),
576 ["y", "m", "d", "h", "min", "s", "ms"]))
577 return ([j for i in map(self._Int32, components) for j in i] +
578 self._Variable(name, "new Date(%s)" % ", ".join(components)))
579
580 def _PlainFunction(self, name, recursion_budget):
581 result_name = "result"
582 body = ["function() {"]
583 body += self._Object(result_name, recursion_budget - 1)
584 body.append("return result;\n}")
585 return self._Variable(name, "%s" % "\n".join(body))
586
587 def _JSFunction(self, name, recursion_budget):
588 result = self._PlainFunction(name, recursion_budget)
589 self._AddAccessors(name, result, recursion_budget)
590 self._AddProperties(name, result, recursion_budget)
591 self._AddElements(name, result, recursion_budget)
592 return result
593
594 def _JSFunctionProxy(self, name, recursion_budget):
595 # TODO(jkummerow): Revisit this as the Proxy implementation evolves.
596 return self._Variable(name, "Proxy.createFunction(%s, function() {})" %
597 self.PROXY_TRAPS)
598
599 def _JSGeneratorObject(self, name, recursion_budget):
600 # TODO(jkummerow): Be more creative here?
601 return self._Variable(name, "(function*() { yield 1; })()")
602
603 def _JSMap(self, name, recursion_budget, weak=""):
604 result = self._Variable(name, "new %sMap()" % weak)
605 num_entries = random.randint(0, 3)
606 for i in range(num_entries):
607 key_name = "%s_k%d" % (name, i)
608 value_name = "%s_v%d" % (name, i)
609 if weak:
610 result += self._JSObject(key_name, recursion_budget - 1)
611 else:
612 result += self._Object(key_name, recursion_budget - 1)
613 result += self._Object(value_name, recursion_budget - 1)
614 result.append("%s.set(%s, %s)" % (name, key_name, value_name))
615 return result
616
617 def _JSMapIterator(self, name, recursion_budget):
618 map_name = name + "_map"
619 result = self._JSMap(map_name, recursion_budget)
620 iterator_type = random.choice(['keys', 'values', 'entries'])
621 return (result + self._Variable(name, "%s.%s()" %
622 (map_name, iterator_type)))
623
624 def _JSProxy(self, name, recursion_budget):
625 # TODO(jkummerow): Revisit this as the Proxy implementation evolves.
626 return self._Variable(name, "Proxy.create(%s)" % self.PROXY_TRAPS)
627
628 def _JSRegExp(self, name, recursion_budget):
629 flags = random.choice(["", "g", "i", "m", "gi"])
630 string = "a(b|c)*a" # TODO(jkummerow): Be more creative here?
631 ctor = random.choice(["/%s/%s", "new RegExp(\"%s\", \"%s\")"])
632 return self._Variable(name, ctor % (string, flags))
633
634 def _JSSet(self, name, recursion_budget, weak=""):
635 result = self._Variable(name, "new %sSet()" % weak)
636 num_entries = random.randint(0, 3)
637 for i in range(num_entries):
638 element_name = "%s_e%d" % (name, i)
639 if weak:
640 result += self._JSObject(element_name, recursion_budget - 1)
641 else:
642 result += self._Object(element_name, recursion_budget - 1)
643 result.append("%s.add(%s)" % (name, element_name))
644 return result
645
646 def _JSSetIterator(self, name, recursion_budget):
647 set_name = name + "_set"
648 result = self._JSSet(set_name, recursion_budget)
649 iterator_type = random.choice(['values', 'entries'])
650 return (result + self._Variable(name, "%s.%s()" %
651 (set_name, iterator_type)))
652
653 def _JSTypedArray(self, name, recursion_budget):
654 arraytype = random.choice(["Int8", "Int16", "Int32", "Uint8", "Uint16",
655 "Uint32", "Float32", "Float64", "Uint8Clamped"])
656 ctor_type = random.randint(0, 3)
657 if ctor_type == 0:
658 length = random.randint(0, 1000)
659 return self._Variable(name, "new %sArray(%d)" % (arraytype, length),
660 fallback="new %sArray(8)" % arraytype)
661 elif ctor_type == 1:
662 input_name = name + "_typedarray"
663 result = self._JSTypedArray(input_name, recursion_budget - 1)
664 return (result +
665 self._Variable(name, "new %sArray(%s)" % (arraytype, input_name),
666 fallback="new %sArray(8)" % arraytype))
667 elif ctor_type == 2:
668 arraylike_name = name + "_arraylike"
669 result = self._JSObject(arraylike_name, recursion_budget - 1)
670 length = random.randint(0, 1000)
671 result.append("try { %s.length = %d; } catch(e) {}" %
672 (arraylike_name, length))
673 return (result +
674 self._Variable(name,
675 "new %sArray(%s)" % (arraytype, arraylike_name),
676 fallback="new %sArray(8)" % arraytype))
677 else:
678 die = random.random()
679 buffer_name = name + "_buffer"
680 args = [buffer_name]
681 result = self._JSArrayBuffer(buffer_name, recursion_budget)
682 if die < 0.67:
683 offset_name = name + "_offset"
684 args.append(offset_name)
685 result += self._Int32(offset_name)
686 if die < 0.33:
687 length_name = name + "_length"
688 args.append(length_name)
689 result += self._Int32(length_name)
690 return (result +
691 self._Variable(name,
692 "new %sArray(%s)" % (arraytype, ", ".join(args)),
693 fallback="new %sArray(8)" % arraytype))
694
695 def _JSArrayBufferView(self, name, recursion_budget):
696 if random.random() < 0.4:
697 return self._JSDataView(name, recursion_budget)
698 else:
699 return self._JSTypedArray(name, recursion_budget)
700
701 def _JSWeakCollection(self, name, recursion_budget):
702 ctor = random.choice([self._JSMap, self._JSSet])
703 return ctor(name, recursion_budget, weak="Weak")
704
705 def _PropertyDetails(self, name, recursion_budget):
706 # TODO(jkummerow): Be more clever here?
707 return self._Int32(name)
708
709 def _JSObject(self, name, recursion_budget):
710 die = random.random()
711 if die < 0.4:
712 function = random.choice([self._PlainObject, self._PlainArray,
713 self._PlainFunction])
714 elif die < 0.5:
715 return self._Variable(name, "this") # Global object.
716 else:
717 function = random.choice([self._JSArrayBuffer, self._JSDataView,
718 self._JSDate, self._JSFunctionProxy,
719 self._JSGeneratorObject, self._JSMap,
720 self._JSMapIterator, self._JSRegExp,
721 self._JSSet, self._JSSetIterator,
722 self._JSTypedArray, self._JSValue,
723 self._JSWeakCollection])
724 result = function(name, recursion_budget)
725 self._AddAccessors(name, result, recursion_budget)
726 self._AddProperties(name, result, recursion_budget)
727 self._AddElements(name, result, recursion_budget)
728 return result
729
730 def _JSReceiver(self, name, recursion_budget):
731 if random.random() < 0.9: return self._JSObject(name, recursion_budget)
732 return self._JSProxy(name, recursion_budget)
733
734 def _HeapObject(self, name, recursion_budget):
735 die = random.random()
736 if die < 0.9: return self._JSReceiver(name, recursion_budget)
737 elif die < 0.95: return self._Oddball(name, recursion_budget)
738 else: return self._Name(name, recursion_budget)
739
740 def _Object(self, name, recursion_budget):
741 if recursion_budget <= 0:
742 function = random.choice([self._Oddball, self._Number, self._Name,
743 self._JSValue, self._JSRegExp])
744 return function(name, recursion_budget)
745 if random.random() < 0.2:
746 return self._Smi(name, recursion_budget)
747 return self._HeapObject(name, recursion_budget)
748
749 GENERATORS = {
750 "Boolean": ["true", _Boolean],
751 "HeapObject": ["new Object()", _HeapObject],
752 "Int32": ["32", _Int32],
753 "JSArray": ["new Array()", _JSArray],
754 "JSArrayBuffer": ["new ArrayBuffer(8)", _JSArrayBuffer],
755 "JSArrayBufferView": ["new Int32Array(2)", _JSArrayBufferView],
756 "JSDataView": ["new DataView(new ArrayBuffer(24))", _JSDataView],
757 "JSDate": ["new Date()", _JSDate],
758 "JSFunction": ["function() {}", _JSFunction],
759 "JSFunctionProxy": ["Proxy.createFunction({}, function() {})",
760 _JSFunctionProxy],
761 "JSGeneratorObject": ["(function*(){ yield 1; })()", _JSGeneratorObject],
762 "JSMap": ["new Map()", _JSMap],
763 "JSMapIterator": ["new Map().entries()", _JSMapIterator],
764 "JSObject": ["new Object()", _JSObject],
765 "JSProxy": ["Proxy.create({})", _JSProxy],
766 "JSReceiver": ["new Object()", _JSReceiver],
767 "JSRegExp": ["/ab/g", _JSRegExp],
768 "JSSet": ["new Set()", _JSSet],
769 "JSSetIterator": ["new Set().values()", _JSSetIterator],
770 "JSTypedArray": ["new Int32Array(2)", _JSTypedArray],
771 "JSValue": ["new String('foo')", _JSValue],
772 "JSWeakCollection": ["new WeakMap()", _JSWeakCollection],
773 "Name": ["\"name\"", _Name],
774 "Number": ["1.5", _Number],
775 "Object": ["new Object()", _Object],
776 "PropertyDetails": ["513", _PropertyDetails],
777 "SeqOneByteString": ["\"seq 1-byte\"", _SeqString],
778 "SeqString": ["\"seqstring\"", _SeqString],
779 "SeqTwoByteString": ["\"seq \\u2082-byte\"", _SeqTwoByteString],
780 "Smi": ["1", _Smi],
781 "StrictMode": ["1", _StrictMode],
782 "String": ["\"foo\"", _String],
783 "Symbol": ["Symbol(\"symbol\")", _Symbol],
784 "Uint32": ["32", _Uint32],
785 }
786
787
788 class ArgParser(object):
789 def __init__(self, regex, ctor):
790 self.regex = regex
791 self.ArgCtor = ctor
792
793
794 class Arg(object):
795 def __init__(self, typename, varname, index):
796 self.type = typename
797 self.name = "_%s" % varname
798 self.index = index
799 27
800 28
801 class Function(object): 29 class Function(object):
802 def __init__(self, match): 30 def __init__(self, match):
803 self.name = match.group(1) 31 self.name = match.group(1)
804 self.argslength = -1
805 self.args = {}
806 self.inline = ""
807
808 handle_arg_parser = ArgParser(
809 re.compile("^\s*CONVERT_ARG_HANDLE_CHECKED\((\w+), (\w+), (\d+)\)"),
810 lambda match: Arg(match.group(1), match.group(2), int(match.group(3))))
811
812 plain_arg_parser = ArgParser(
813 re.compile("^\s*CONVERT_ARG_CHECKED\((\w+), (\w+), (\d+)\)"),
814 lambda match: Arg(match.group(1), match.group(2), int(match.group(3))))
815
816 number_handle_arg_parser = ArgParser(
817 re.compile("^\s*CONVERT_NUMBER_ARG_HANDLE_CHECKED\((\w+), (\d+)\)"),
818 lambda match: Arg("Number", match.group(1), int(match.group(2))))
819
820 smi_arg_parser = ArgParser(
821 re.compile("^\s*CONVERT_SMI_ARG_CHECKED\((\w+), (\d+)\)"),
822 lambda match: Arg("Smi", match.group(1), int(match.group(2))))
823
824 double_arg_parser = ArgParser(
825 re.compile("^\s*CONVERT_DOUBLE_ARG_CHECKED\((\w+), (\d+)\)"),
826 lambda match: Arg("Number", match.group(1), int(match.group(2))))
827
828 number_arg_parser = ArgParser(
829 re.compile(
830 "^\s*CONVERT_NUMBER_CHECKED\(\w+, (\w+), (\w+), args\[(\d+)\]\)"),
831 lambda match: Arg(match.group(2), match.group(1), int(match.group(3))))
832
833 strict_mode_arg_parser = ArgParser(
834 re.compile("^\s*CONVERT_STRICT_MODE_ARG_CHECKED\((\w+), (\d+)\)"),
835 lambda match: Arg("StrictMode", match.group(1), int(match.group(2))))
836
837 boolean_arg_parser = ArgParser(
838 re.compile("^\s*CONVERT_BOOLEAN_ARG_CHECKED\((\w+), (\d+)\)"),
839 lambda match: Arg("Boolean", match.group(1), int(match.group(2))))
840
841 property_details_parser = ArgParser(
842 re.compile("^\s*CONVERT_PROPERTY_DETAILS_CHECKED\((\w+), (\d+)\)"),
843 lambda match: Arg("PropertyDetails", match.group(1), int(match.group(2))))
844
845 arg_parsers = [handle_arg_parser, plain_arg_parser, number_handle_arg_parser,
846 smi_arg_parser,
847 double_arg_parser, number_arg_parser, strict_mode_arg_parser,
848 boolean_arg_parser, property_details_parser]
849
850 def SetArgsLength(self, match):
851 self.argslength = int(match.group(1))
852
853 def TryParseArg(self, line):
854 for parser in Function.arg_parsers:
855 match = parser.regex.match(line)
856 if match:
857 arg = parser.ArgCtor(match)
858 self.args[arg.index] = arg
859 return True
860 return False
861
862 def Filename(self):
863 return "%s.js" % self.name.lower()
864
865 def __str__(self):
866 s = [self.name, "("]
867 argcount = self.argslength
868 if argcount < 0:
869 print("WARNING: unknown argslength for function %s" % self.name)
870 if self.args:
871 argcount = max([self.args[i].index + 1 for i in self.args])
872 else:
873 argcount = 0
874 for i in range(argcount):
875 if i > 0: s.append(", ")
876 s.append(self.args[i].type if i in self.args else "<unknown>")
877 s.append(")")
878 return "".join(s)
879 32
880 33
881 class Macro(object): 34 class Macro(object):
882 def __init__(self, match): 35 def __init__(self, match):
883 self.name = match.group(1) 36 self.name = match.group(1)
884 self.args = [s.strip() for s in match.group(2).split(",")] 37 self.args = [s.strip() for s in match.group(2).split(",")]
885 self.lines = [] 38 self.lines = []
886 self.indentation = 0 39 self.indentation = 0
887 self.AddLine(match.group(3)) 40 self.AddLine(match.group(3))
888 41
(...skipping 21 matching lines...) Expand all
910 filler = {} 63 filler = {}
911 assert len(arg_values) == len(self.args) 64 assert len(arg_values) == len(self.args)
912 for i in range(len(self.args)): 65 for i in range(len(self.args)):
913 filler[self.args[i]] = arg_values[i] 66 filler[self.args[i]] = arg_values[i]
914 result = [] 67 result = []
915 for line in self.lines: 68 for line in self.lines:
916 result.append(line % filler) 69 result.append(line % filler)
917 return result 70 return result
918 71
919 72
920 # Parses HEADERFILENAME to find out which runtime functions are "inline".
921 def FindInlineRuntimeFunctions():
922 inline_functions = []
923 with open(HEADERFILENAME, "r") as f:
924 inline_list = "#define INLINE_FUNCTION_LIST(F) \\\n"
925 inline_function = re.compile(r"^\s*F\((\w+), \d+, \d+\)\s*\\?")
926 mode = "SEARCHING"
927 for line in f:
928 if mode == "ACTIVE":
929 match = inline_function.match(line)
930 if match:
931 inline_functions.append(match.group(1))
932 if not line.endswith("\\\n"):
933 mode = "SEARCHING"
934 elif mode == "SEARCHING":
935 if line == inline_list:
936 mode = "ACTIVE"
937 return inline_functions
938
939
940 def ReadFileAndExpandMacros(filename): 73 def ReadFileAndExpandMacros(filename):
941 found_macros = {} 74 found_macros = {}
942 expanded_lines = [] 75 expanded_lines = []
943 with open(filename, "r") as f: 76 with open(filename, "r") as f:
944 found_macro = None 77 found_macro = None
945 for line in f: 78 for line in f:
946 if found_macro is not None: 79 if found_macro is not None:
947 found_macro.AddLine(line) 80 found_macro.AddLine(line)
948 if not line.endswith("\\\n"): 81 if not line.endswith("\\\n"):
949 found_macro.Finalize() 82 found_macro.Finalize()
(...skipping 19 matching lines...) Expand all
969 args = [s.strip() for s in match.group(1).split(",")] 102 args = [s.strip() for s in match.group(1).split(",")]
970 expanded_lines += found_macros[first_word].FillIn(args) 103 expanded_lines += found_macros[first_word].FillIn(args)
971 continue 104 continue
972 105
973 expanded_lines.append(line) 106 expanded_lines.append(line)
974 return expanded_lines 107 return expanded_lines
975 108
976 109
977 # Detects runtime functions by parsing FILENAME. 110 # Detects runtime functions by parsing FILENAME.
978 def FindRuntimeFunctions(): 111 def FindRuntimeFunctions():
979 inline_functions = FindInlineRuntimeFunctions()
980 functions = [] 112 functions = []
981 expanded_lines = ReadFileAndExpandMacros(FILENAME) 113 expanded_lines = ReadFileAndExpandMacros(FILENAME)
982 function = None 114 function = None
983 partial_line = "" 115 partial_line = ""
984 for line in expanded_lines: 116 for line in expanded_lines:
985 # Multi-line definition support, ignoring macros. 117 # Multi-line definition support, ignoring macros.
986 if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"): 118 if line.startswith("RUNTIME_FUNCTION") and not line.endswith("{\n"):
987 if line.endswith("\\\n"): continue 119 if line.endswith("\\\n"): continue
988 partial_line = line.rstrip() 120 partial_line = line.rstrip()
989 continue 121 continue
990 if partial_line: 122 if partial_line:
991 partial_line += " " + line.strip() 123 partial_line += " " + line.strip()
992 if partial_line.endswith("{"): 124 if partial_line.endswith("{"):
993 line = partial_line 125 line = partial_line
994 partial_line = "" 126 partial_line = ""
995 else: 127 else:
996 continue 128 continue
997 129
998 match = FUNCTION.match(line) 130 match = FUNCTION.match(line)
999 if match: 131 if match:
1000 function = Function(match) 132 function = Function(match)
1001 if function.name in inline_functions:
1002 function.inline = "_"
1003 continue 133 continue
1004 if function is None: continue 134 if function is None: continue
1005 135
1006 match = ARGSLENGTH.match(line)
1007 if match:
1008 function.SetArgsLength(match)
1009 continue
1010
1011 if function.TryParseArg(line):
1012 continue
1013
1014 if line == FUNCTIONEND: 136 if line == FUNCTIONEND:
1015 if function is not None: 137 if function is not None:
1016 functions.append(function) 138 functions.append(function)
1017 function = None 139 function = None
1018 return functions 140 return functions
1019 141
1020 142
1021 # Hack: This must have the same fields as class Function above, because the
1022 # two are used polymorphically in RunFuzzer(). We could use inheritance...
1023 class Builtin(object): 143 class Builtin(object):
1024 def __init__(self, match): 144 def __init__(self, match):
1025 self.name = match.group(1) 145 self.name = match.group(1)
1026 args = match.group(2)
1027 self.argslength = 0 if args == "" else args.count(",") + 1
1028 self.inline = ""
1029 self.args = {}
1030 if self.argslength > 0:
1031 args = args.split(",")
1032 for i in range(len(args)):
1033 # a = args[i].strip() # TODO: filter out /* comments */ first.
1034 a = ""
1035 self.args[i] = Arg("Object", a, i)
1036
1037 def __str__(self):
1038 return "%s(%d)" % (self.name, self.argslength)
1039 146
1040 147
1041 def FindJSBuiltins(): 148 def FindJSNatives():
1042 PATH = "src" 149 PATH = "src"
1043 fileslist = [] 150 fileslist = []
1044 for (root, dirs, files) in os.walk(PATH): 151 for (root, dirs, files) in os.walk(PATH):
1045 for f in files: 152 for f in files:
1046 if f.endswith(".js"): 153 if f.endswith(".js"):
1047 fileslist.append(os.path.join(root, f)) 154 fileslist.append(os.path.join(root, f))
1048 builtins = [] 155 natives = []
1049 regexp = re.compile("^function (\w+)\s*\((.*?)\) {") 156 regexp = re.compile("^function (\w+)\s*\((.*?)\) {")
1050 matches = 0 157 matches = 0
1051 for filename in fileslist: 158 for filename in fileslist:
1052 with open(filename, "r") as f: 159 with open(filename, "r") as f:
1053 file_contents = f.read() 160 file_contents = f.read()
1054 file_contents = js2c.ExpandInlineMacros(file_contents) 161 file_contents = js2c.ExpandInlineMacros(file_contents)
1055 lines = file_contents.split("\n") 162 lines = file_contents.split("\n")
1056 partial_line = "" 163 partial_line = ""
1057 for line in lines: 164 for line in lines:
1058 if line.startswith("function") and not '{' in line: 165 if line.startswith("function") and not '{' in line:
1059 partial_line += line.rstrip() 166 partial_line += line.rstrip()
1060 continue 167 continue
1061 if partial_line: 168 if partial_line:
1062 partial_line += " " + line.strip() 169 partial_line += " " + line.strip()
1063 if '{' in line: 170 if '{' in line:
1064 line = partial_line 171 line = partial_line
1065 partial_line = "" 172 partial_line = ""
1066 else: 173 else:
1067 continue 174 continue
1068 match = regexp.match(line) 175 match = regexp.match(line)
1069 if match: 176 if match:
1070 builtins.append(Builtin(match)) 177 natives.append(Builtin(match))
1071 return builtins 178 return natives
1072
1073
1074 # Classifies runtime functions.
1075 def ClassifyFunctions(functions):
1076 # Can be fuzzed with a JavaScript testcase.
1077 js_fuzzable_functions = []
1078 # We have enough information to fuzz these, but they need inputs that
1079 # cannot be created or passed around in JavaScript.
1080 cctest_fuzzable_functions = []
1081 # This script does not have enough information about these.
1082 unknown_functions = []
1083
1084 types = {}
1085 for f in functions:
1086 if f.name in BLACKLISTED:
1087 continue
1088 decision = js_fuzzable_functions
1089 custom = CUSTOM_KNOWN_GOOD_INPUT.get(f.name, None)
1090 if f.argslength < 0:
1091 # Unknown length -> give up unless there's a custom definition.
1092 if custom and custom[-1] is not None:
1093 f.argslength = custom[-1]
1094 assert len(custom) == f.argslength + 1, \
1095 ("%s: last custom definition must be argslength" % f.name)
1096 else:
1097 decision = unknown_functions
1098 else:
1099 if custom:
1100 # Any custom definitions must match the known argslength.
1101 assert len(custom) == f.argslength + 1, \
1102 ("%s should have %d custom definitions but has %d" %
1103 (f.name, f.argslength + 1, len(custom)))
1104 for i in range(f.argslength):
1105 if custom and custom[i] is not None:
1106 # All good, there's a custom definition.
1107 pass
1108 elif not i in f.args:
1109 # No custom definition and no parse result -> give up.
1110 decision = unknown_functions
1111 else:
1112 t = f.args[i].type
1113 if t in NON_JS_TYPES:
1114 decision = cctest_fuzzable_functions
1115 else:
1116 assert Generator.IsTypeSupported(t), \
1117 ("type generator not found for %s, function: %s" % (t, f))
1118 decision.append(f)
1119 return (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions)
1120
1121
1122 def _GetKnownGoodArgs(function, generator):
1123 custom_input = CUSTOM_KNOWN_GOOD_INPUT.get(function.name, None)
1124 definitions = []
1125 argslist = []
1126 for i in range(function.argslength):
1127 if custom_input and custom_input[i] is not None:
1128 name = "arg%d" % i
1129 definitions.append("var %s = %s;" % (name, custom_input[i]))
1130 else:
1131 arg = function.args[i]
1132 name = arg.name
1133 definitions += generator.RandomVariable(name, arg.type, simple=True)
1134 argslist.append(name)
1135 return (definitions, argslist)
1136
1137
1138 def _GenerateTestcase(function, definitions, argslist, throws):
1139 s = ["// Copyright 2014 the V8 project authors. All rights reserved.",
1140 "// AUTO-GENERATED BY tools/generate-runtime-tests.py, DO NOT MODIFY",
1141 "// Flags: --allow-natives-syntax --harmony --harmony-proxies"
1142 ] + definitions
1143 call = "%%%s%s(%s);" % (function.inline, function.name, ", ".join(argslist))
1144 if throws:
1145 s.append("try {")
1146 s.append(call);
1147 s.append("} catch(e) {}")
1148 else:
1149 s.append(call)
1150 testcase = "\n".join(s)
1151 return testcase
1152
1153
1154 def GenerateJSTestcaseForFunction(function):
1155 gen = Generator()
1156 (definitions, argslist) = _GetKnownGoodArgs(function, gen)
1157 testcase = _GenerateTestcase(function, definitions, argslist,
1158 function.name in THROWS)
1159 path = os.path.join(BASEPATH, function.Filename())
1160 with open(path, "w") as f:
1161 f.write("%s\n" % testcase)
1162
1163
1164 def GenerateTestcases(functions):
1165 shutil.rmtree(BASEPATH) # Re-generate everything.
1166 os.makedirs(BASEPATH)
1167 for f in functions:
1168 GenerateJSTestcaseForFunction(f)
1169
1170
1171 def _SaveFileName(save_path, process_id, save_file_index):
1172 return "%s/fuzz_%d_%d.js" % (save_path, process_id, save_file_index)
1173
1174
1175 def _GetFuzzableRuntimeFunctions():
1176 functions = FindRuntimeFunctions()
1177 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \
1178 ClassifyFunctions(functions)
1179 return js_fuzzable_functions
1180
1181
1182 FUZZ_TARGET_LISTS = {
1183 "runtime": _GetFuzzableRuntimeFunctions,
1184 "builtins": FindJSBuiltins,
1185 }
1186
1187
1188 def RunFuzzer(process_id, options, stop_running):
1189 MAX_SLEEP_TIME = 0.1
1190 INITIAL_SLEEP_TIME = 0.001
1191 SLEEP_TIME_FACTOR = 1.25
1192 base_file_name = "/dev/shm/runtime_fuzz_%d" % process_id
1193 test_file_name = "%s.js" % base_file_name
1194 stderr_file_name = "%s.out" % base_file_name
1195 save_file_index = 0
1196 while os.path.exists(_SaveFileName(options.save_path, process_id,
1197 save_file_index)):
1198 save_file_index += 1
1199
1200 targets = FUZZ_TARGET_LISTS[options.fuzz_target]()
1201 try:
1202 for i in range(options.num_tests):
1203 if stop_running.is_set(): break
1204 function = None
1205 while function is None or function.argslength == 0:
1206 function = random.choice(targets)
1207 args = []
1208 definitions = []
1209 gen = Generator()
1210 for i in range(function.argslength):
1211 arg = function.args[i]
1212 argname = "arg%d%s" % (i, arg.name)
1213 args.append(argname)
1214 definitions += gen.RandomVariable(argname, arg.type, simple=False)
1215 testcase = _GenerateTestcase(function, definitions, args, True)
1216 with open(test_file_name, "w") as f:
1217 f.write("%s\n" % testcase)
1218 with open("/dev/null", "w") as devnull:
1219 with open(stderr_file_name, "w") as stderr:
1220 process = subprocess.Popen(
1221 [options.binary, "--allow-natives-syntax", "--harmony",
1222 "--harmony-proxies", "--enable-slow-asserts", test_file_name],
1223 stdout=devnull, stderr=stderr)
1224 end_time = time.time() + options.timeout
1225 timed_out = False
1226 exit_code = None
1227 sleep_time = INITIAL_SLEEP_TIME
1228 while exit_code is None:
1229 if time.time() >= end_time:
1230 # Kill the process and wait for it to exit.
1231 os.kill(process.pid, signal.SIGTERM)
1232 exit_code = process.wait()
1233 timed_out = True
1234 else:
1235 exit_code = process.poll()
1236 time.sleep(sleep_time)
1237 sleep_time = sleep_time * SLEEP_TIME_FACTOR
1238 if sleep_time > MAX_SLEEP_TIME:
1239 sleep_time = MAX_SLEEP_TIME
1240 if exit_code != 0 and not timed_out:
1241 oom = False
1242 with open(stderr_file_name, "r") as stderr:
1243 for line in stderr:
1244 if line.strip() == "# Allocation failed - process out of memory":
1245 oom = True
1246 break
1247 if oom: continue
1248 save_name = _SaveFileName(options.save_path, process_id,
1249 save_file_index)
1250 shutil.copyfile(test_file_name, save_name)
1251 save_file_index += 1
1252 except KeyboardInterrupt:
1253 stop_running.set()
1254 finally:
1255 if os.path.exists(test_file_name):
1256 os.remove(test_file_name)
1257 if os.path.exists(stderr_file_name):
1258 os.remove(stderr_file_name)
1259
1260
1261 def BuildOptionParser():
1262 usage = """Usage: %%prog [options] ACTION
1263
1264 where ACTION can be:
1265
1266 info Print diagnostic info.
1267 check Check that runtime functions can be parsed as expected, and that
1268 test cases exist.
1269 generate Parse source code for runtime functions, and auto-generate
1270 test cases for them. Warning: this will nuke and re-create
1271 %(path)s.
1272 fuzz Generate fuzz tests, run them, save those that crashed (see options).
1273 """ % {"path": os.path.relpath(BASEPATH)}
1274
1275 o = optparse.OptionParser(usage=usage)
1276 o.add_option("--binary", default="out/x64.debug/d8",
1277 help="d8 binary used for running fuzz tests (default: %default)")
1278 o.add_option("--fuzz-target", default="runtime",
1279 help="Set of functions targeted by fuzzing. Allowed values: "
1280 "%s (default: %%default)" % ", ".join(FUZZ_TARGET_LISTS))
1281 o.add_option("-n", "--num-tests", default=1000, type="int",
1282 help="Number of fuzz tests to generate per worker process"
1283 " (default: %default)")
1284 o.add_option("--save-path", default="~/runtime_fuzz_output",
1285 help="Path to directory where failing tests will be stored"
1286 " (default: %default)")
1287 o.add_option("--timeout", default=20, type="int",
1288 help="Timeout for each fuzz test (in seconds, default:"
1289 "%default)")
1290 return o
1291
1292
1293 def ProcessOptions(options, args):
1294 options.save_path = os.path.expanduser(options.save_path)
1295 if options.fuzz_target not in FUZZ_TARGET_LISTS:
1296 print("Invalid fuzz target: %s" % options.fuzz_target)
1297 return False
1298 if len(args) != 1 or args[0] == "help":
1299 return False
1300 return True
1301 179
1302 180
1303 def Main(): 181 def Main():
1304 parser = BuildOptionParser() 182 functions = FindRuntimeFunctions()
1305 (options, args) = parser.parse_args() 183 natives = FindJSNatives()
184 errors = 0
185 runtime_map = {}
186 for f in functions:
187 runtime_map[f.name] = 1
188 for b in natives:
189 if b.name in runtime_map:
190 print("JS_Native/Runtime_Function name clash: %s" % b.name)
191 errors += 1
1306 192
1307 if not ProcessOptions(options, args): 193 if errors > 0:
1308 parser.print_help()
1309 return 1 194 return 1
1310 action = args[0] 195 print("Runtime/Natives name clashes: checked %d/%d functions, all good." %
196 (len(functions), len(natives)))
197 return 0
1311 198
1312 functions = FindRuntimeFunctions()
1313 (js_fuzzable_functions, cctest_fuzzable_functions, unknown_functions) = \
1314 ClassifyFunctions(functions)
1315 builtins = FindJSBuiltins()
1316
1317 if action == "test":
1318 print("put your temporary debugging code here")
1319 return 0
1320
1321 if action == "info":
1322 print("%d functions total; js_fuzzable_functions: %d, "
1323 "cctest_fuzzable_functions: %d, unknown_functions: %d"
1324 % (len(functions), len(js_fuzzable_functions),
1325 len(cctest_fuzzable_functions), len(unknown_functions)))
1326 print("%d JavaScript builtins" % len(builtins))
1327 print("unknown functions:")
1328 for f in unknown_functions:
1329 print(f)
1330 return 0
1331
1332 if action == "check":
1333 errors = 0
1334
1335 def CheckCount(actual, expected, description):
1336 if len(actual) != expected:
1337 print("Expected to detect %d %s, but found %d." % (
1338 expected, description, len(actual)))
1339 print("If this change is intentional, please update the expectations"
1340 " at the top of %s." % THIS_SCRIPT)
1341 return 1
1342 return 0
1343
1344 errors += CheckCount(functions, EXPECTED_FUNCTION_COUNT,
1345 "functions in total")
1346 errors += CheckCount(js_fuzzable_functions, EXPECTED_FUZZABLE_COUNT,
1347 "JavaScript-fuzzable functions")
1348 errors += CheckCount(cctest_fuzzable_functions, EXPECTED_CCTEST_COUNT,
1349 "cctest-fuzzable functions")
1350 errors += CheckCount(unknown_functions, EXPECTED_UNKNOWN_COUNT,
1351 "functions with incomplete type information")
1352 errors += CheckCount(builtins, EXPECTED_BUILTINS_COUNT,
1353 "JavaScript builtins")
1354
1355 def CheckTestcasesExisting(functions):
1356 errors = 0
1357 for f in functions:
1358 if not os.path.isfile(os.path.join(BASEPATH, f.Filename())):
1359 print("Missing testcase for %s, please run '%s generate'" %
1360 (f.name, THIS_SCRIPT))
1361 errors += 1
1362 files = filter(lambda filename: not filename.startswith("."),
1363 os.listdir(BASEPATH))
1364 if (len(files) != len(functions)):
1365 unexpected_files = set(files) - set([f.Filename() for f in functions])
1366 for f in unexpected_files:
1367 print("Unexpected testcase: %s" % os.path.join(BASEPATH, f))
1368 errors += 1
1369 print("Run '%s generate' to automatically clean these up."
1370 % THIS_SCRIPT)
1371 return errors
1372
1373 errors += CheckTestcasesExisting(js_fuzzable_functions)
1374
1375 def CheckNameClashes(runtime_functions, builtins):
1376 errors = 0
1377 runtime_map = {}
1378 for f in runtime_functions:
1379 runtime_map[f.name] = 1
1380 for b in builtins:
1381 if b.name in runtime_map:
1382 print("Builtin/Runtime_Function name clash: %s" % b.name)
1383 errors += 1
1384 return errors
1385
1386 errors += CheckNameClashes(functions, builtins)
1387
1388 if errors > 0:
1389 return 1
1390 print("Generated runtime tests: all good.")
1391 return 0
1392
1393 if action == "generate":
1394 GenerateTestcases(js_fuzzable_functions)
1395 return 0
1396
1397 if action == "fuzz":
1398 processes = []
1399 if not os.path.isdir(options.save_path):
1400 os.makedirs(options.save_path)
1401 stop_running = multiprocessing.Event()
1402 for i in range(multiprocessing.cpu_count()):
1403 args = (i, options, stop_running)
1404 p = multiprocessing.Process(target=RunFuzzer, args=args)
1405 p.start()
1406 processes.append(p)
1407 try:
1408 for i in range(len(processes)):
1409 processes[i].join()
1410 except KeyboardInterrupt:
1411 stop_running.set()
1412 for i in range(len(processes)):
1413 processes[i].join()
1414 return 0
1415 199
1416 if __name__ == "__main__": 200 if __name__ == "__main__":
1417 sys.exit(Main()) 201 sys.exit(Main())
OLDNEW
« no previous file with comments | « tools/check-name-clashes.py ('k') | tools/presubmit.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698