| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import itertools | |
| 7 import 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 Loading... |
| 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 Loading... |
| 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()) |
| OLD | NEW |