text: #! /usr/bin/env python
  
  #   Copyright 2016 WebAssembly Community Group participants
  #
  #   Licensed under the Apache License, Version 2.0 (the "License");
  #   you may not use this file except in compliance with the License.
  #   You may obtain a copy of the License at
  #
  #       http://www.apache.org/licenses/LICENSE-2.0
  #
  #   Unless required by applicable law or agreed to in writing, software
  #   distributed under the License is distributed on an "AS IS" BASIS,
  #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  #   See the License for the specific language governing permissions and
  #   limitations under the License.
  
  '''
  This fuzzes passes, by starting with a working program, then running
  random passes on the wast, and seeing if they break something
  
  Usage: Provide a base filename for a runnable program, e.g. a.out.js.
         Then we will modify a.out.wast. Note that the program must
         be built to run using that wast (BINARYEN_METHOD=interpret-s-expr)
  
         Other parameters after the first are used when calling the program.
  '''
  
  import os
  import random
  import shutil
  import subprocess
  import sys
  
  PASSES = [
      "duplicate-function-elimination",
      "dce",
      "remove-unused-brs",
      "remove-unused-names",
      "optimize-instructions",
      "precompute",
      "simplify-locals",
      "vacuum",
      "coalesce-locals",
      "reorder-locals",
      "merge-blocks",
      "remove-unused-functions",
  ]
  
  # main
  
  base = sys.argv[1]
  wast = base[:-3] + '.wast'
  print '>>> base program:', base, ', wast:', wast
  
  args = sys.argv[2:]
  
  
  def run():
    if os.path.exists(wast):
      print '>>> running using a wast of size', os.stat(wast).st_size
    cmd = ['mozjs', base] + args
    try:
      return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except Exception, e:
      print ">>> !!! ", e, " !!!"
  
  
  original_wast = None
  
  try:
    # get normal output
  
    normal = run()
    print '>>> normal output:\n', normal
    assert normal, 'must be output'
  
    # ensure we actually use the wast
  
    original_wast = wast + '.original.wast'
    shutil.move(wast, original_wast)
    assert run() != normal, 'running without the wast must fail'
  
    # ensure a bad pass makes it fail
  
    def apply_passes(passes):
      wasm_opt = os.path.join('bin', 'wasm-opt')
      subprocess.check_call([wasm_opt, original_wast] + passes + ['-o', wast])
  
    apply_passes(['--remove-imports'])
    assert run() != normal, 'running after a breaking pass must fail'
  
    # loop, looking for failures
  
    def simplify(passes):
      # passes is known to fail, try to simplify down by removing
      more = True
      while more:
        more = False
        print '>>> trying to reduce:', ' '.join(passes),
        print '  [' + str(len(passes)) + ']'
        for i in range(len(passes)):
          smaller = passes[:i] + passes[i + 1:]
          print '>>>>>> try to reduce to:', ' '.join(smaller),
          print '  [' + str(len(smaller)) + ']'
          try:
            apply_passes(smaller)
            assert run() == normal
          except:
            # this failed too, so it's a good reduction
            passes = smaller
            print '>>> reduction successful'
            more = True
            break
      print '>>> reduced to:', ' '.join(passes)
  
    tested = set()
  
    def pick_passes():
      ret = []
      while 1:
        str_ret = str(ret)
        if random.random() < 0.1 and str_ret not in tested:
          tested.add(str_ret)
          return ret
        ret.append('--' + random.choice(PASSES))
  
    counter = 0
  
    while 1:
      passes = pick_passes()
      print '>>> [' + str(counter) + '] testing:', ' '.join(passes)
      counter += 1
      try:
        apply_passes(passes)
      except Exception, e:
        print e
        simplify(passes)
        break
      seen = run()
      if seen != normal:
        print '>>> bad output:\n', seen
        simplify(passes)
        break
  
  finally:
    if original_wast:
      shutil.move(original_wast, wast)
  