class ExecJS::ExternalRuntime

def available?

def available?
  require 'json'
  binary ? true : false
end

def binary

def binary
  @binary ||= which(@command)
end

def deprecated?

def deprecated?
  @deprecated
end

def encode_source(source)

def encode_source(source)
  encoded_source = encode_unicode_codepoints(source)
  ::JSON.generate("(function(){ #{encoded_source} })()", quirks_mode: true)
end

def encode_unicode_codepoints(str)

def encode_unicode_codepoints(str)
  str.gsub(/[\u0080-\uffff]/) do |ch|
    "\\u%04x" % ch.codepoints.to_a
  end
end

def exec_runtime(filename)

def exec_runtime(filename)
  path = Dir::Tmpname.create(['execjs', 'json']) {}
  begin
    command = binary.split(" ") << filename
    `#{shell_escape(*command)} 2>&1 > #{path}`
    output = File.open(path, 'rb', **@popen_options) { |f| f.read }
  ensure
    File.unlink(path) if path
  end
  if $?.success?
    output
  else
    raise exec_runtime_error(output)
  end
end

def exec_runtime(filename)

def exec_runtime(filename)
  command = "#{Shellwords.join(binary.split(' ') << filename)}"
  io = IO.popen(command, **@popen_options)
  output = io.read
  io.close
  if $?.success?
    output
  else
    raise exec_runtime_error(output)
  end
end

def exec_runtime(filename)

def exec_runtime(filename)
  io = IO.popen(binary.split(' ') << filename, **@popen_options)
  output = io.read
  io.close
  if $?.success?
    output
  else
    raise exec_runtime_error(output)
  end
end

def exec_runtime_error(output)

def exec_runtime_error(output)
  error = RuntimeError.new(output)
  lines = output.split("\n")
  lineno = lines[0][/:(\d+)$/, 1] if lines[0]
  lineno ||= 1
  error.set_backtrace(["(execjs):#{lineno}"] + caller)
  error
end

def initialize(options)

def initialize(options)
  @name        = options[:name]
  @command     = options[:command]
  @runner_path = options[:runner_path]
  @encoding    = options[:encoding]
  @deprecated  = !!options[:deprecated]
  @binary      = nil
  @popen_options = {}
  @popen_options[:external_encoding] = @encoding if @encoding
  @popen_options[:internal_encoding] = ::Encoding.default_internal || 'UTF-8'
  if @runner_path
    instance_eval <<~RUBY, __FILE__, __LINE__
      def compile_source(source)
        <<-RUNNER
        #{IO.read(@runner_path)}
        RUNNER
      end
    RUBY
  end
end

def json2_source

def json2_source
  @json2_source ||= IO.read(ExecJS.root + "/support/json2.js")
end

def locate_executable(command)

def locate_executable(command)
  commands = Array(command)
  if ExecJS.windows? && File.extname(command) == ""
    ENV['PATHEXT'].split(File::PATH_SEPARATOR).each { |p|
      commands << (command + p)
    }
  end
  commands.find { |cmd|
    if File.executable? cmd
      cmd
    else
      path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |p|
        full_path = File.join(p, cmd)
        File.executable?(full_path) && File.file?(full_path)
      }
      path && File.expand_path(cmd, path)
    end
  }
end

def shell_escape(*args)

def shell_escape(*args)
  # see http://technet.microsoft.com/en-us/library/cc723564.aspx#XSLTsection123121120120
  args.map { |arg|
    arg = %Q("#{arg.gsub('"','""')}") if arg.match(/[&|()<>^ "]/)
    arg
  }.join(" ")
end

def which(command)

def which(command)
  Array(command).find do |name|
    name, args = name.split(/\s+/, 2)
    path = locate_executable(name)
    next unless path
    args ? "#{path} #{args}" : path
  end
end