class Sqreen::Js::MiniRacerExecutableJs

Public Class Methods

add_code(code_id, code) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 87
def add_code(code_id, code)
  # It's important that the definition is run in its own scope (by executing it inside an anonymous function)
  # Otherwise some auxiliary functions that the backend server sends will collide the name
  # Because they're defined with `var`, running the definitions inside a function is enough
  eval_unsafe "(function() { #{code} })()"
  transf_global_funcs code_id
  @code_ids ||= Set.new
  @code_ids << code_id
rescue StandardError
  @failed_code_ids ||= Set.new
  @failed_code_ids << code_id
  raise
end
code?(code_id) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 77
def code?(code_id)
  return false unless @code_ids
  @code_ids.include?(code_id)
end
code_failed?(code_id) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 82
def code_failed?(code_id)
  return false unless @failed_code_ids
  @failed_code_ids.include?(code_id)
end
code_id(code) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 64
def self.code_id(code)
  Digest::MD5.hexdigest(code)
end
define_sqreen_context(modoole) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 69
        def define_sqreen_context(modoole)
          # Context specialized for Sqreen usage
          Sqreen::Js.const_set 'SqreenContext', Class.new(modoole.const_get('Context'))
          SqreenContext.class_eval do
            attr_accessor :gc_threshold_in_bytes
            attr_accessor :gc_load
            attr_writer   :timeout

            def code?(code_id)
              return false unless @code_ids
              @code_ids.include?(code_id)
            end

            def code_failed?(code_id)
              return false unless @failed_code_ids
              @failed_code_ids.include?(code_id)
            end

            def add_code(code_id, code)
              # It's important that the definition is run in its own scope (by executing it inside an anonymous function)
              # Otherwise some auxiliary functions that the backend server sends will collide the name
              # Because they're defined with `var`, running the definitions inside a function is enough
              eval_unsafe "(function() { #{code} })()"
              transf_global_funcs code_id
              @code_ids ||= Set.new
              @code_ids << code_id
            rescue StandardError
              @failed_code_ids ||= Set.new
              @failed_code_ids << code_id
              raise
            end

            def eval_unsafe(str, filename = nil, timeoutv = nil)
              # Beware, timeout could be kept in the context
              # if perf cap is removed after having been activated
              # As it's unused by execjscb we are not cleaning it
              return super(str, filename) if timeoutv.nil?
              return if timeoutv <= 0.0
              timeoutv *= 1000 # Timeout are currently expressed in seconds
              @timeout = timeoutv
              @eval_thread = Thread.current
              timeout do
                super(str, filename)
              end
            end

            def possibly_gc
              @gc_threshold_in_bytes ||= DEFAULT_GC_THRESHOLD
              @gc_load ||= 0

              # garbage collections max 1 in every 4 calls (avg)
              if heap_stats[:total_heap_size] > @gc_threshold_in_bytes
                low_memory_notification
                @gc_load += 4
              else
                @gc_load = [0, @gc_load - 1].max
              end
            end

            private

            def transf_global_funcs(code_id)
              # Multiple callbacks may share the same name. In order to avoid collisions, we rename them here.
              eval_unsafe <<-JS
                Object.keys(this).forEach(name => {
                  if (typeof this[name] === "function" && !name.startsWith("sqreen_")) {
                    this['sqreen_#{code_id}_' + name] = this[name];
                    this[name] = undefined;
                  }
                });
              JS
            end
          end
        end
eval_unsafe(str, filename = nil, timeoutv = nil) click to toggle source
Calls superclass method
# File lib/sqreen/js/mini_racer_executable_js.rb, line 101
def eval_unsafe(str, filename = nil, timeoutv = nil)
  # Beware, timeout could be kept in the context
  # if perf cap is removed after having been activated
  # As it's unused by execjscb we are not cleaning it
  return super(str, filename) if timeoutv.nil?
  return if timeoutv <= 0.0
  timeoutv *= 1000 # Timeout are currently expressed in seconds
  @timeout = timeoutv
  @eval_thread = Thread.current
  timeout do
    super(str, filename)
  end
end
new(pool, code, vendored) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 24
def initialize(pool, code, vendored)
  @pool = pool
  @code = code
  @code_id = self.class.code_id(code)

  @module = vendored ? Sqreen::MiniRacer : MiniRacer

  mod = vendored ? Sqreen::MiniRacer : MiniRacer

  return if ctx_defined?

  self.class.define_sqreen_context(mod)
  define_ctx!
end
possibly_gc() click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 115
def possibly_gc
  @gc_threshold_in_bytes ||= DEFAULT_GC_THRESHOLD
  @gc_load ||= 0

  # garbage collections max 1 in every 4 calls (avg)
  if heap_stats[:total_heap_size] > @gc_threshold_in_bytes
    low_memory_notification
    @gc_load += 4
  else
    @gc_load = [0, @gc_load - 1].max
  end
end
transf_global_funcs(code_id) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 130
            def transf_global_funcs(code_id)
              # Multiple callbacks may share the same name. In order to avoid collisions, we rename them here.
              eval_unsafe <<-JS
                Object.keys(this).forEach(name => {
                  if (typeof this[name] === "function" && !name.startsWith("sqreen_")) {
                    this['sqreen_#{code_id}_' + name] = this[name];
                    this[name] = undefined;
                  }
                });
              JS
            end

Public Instance Methods

ctx_defined?() click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 16
def ctx_defined?
  @@ctx_defined
end
define_ctx!() click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 20
def define_ctx!
  @@ctx_defined = true # rubocop:disable Style/ClassVars
end
run_js_cb(cb_name, budget, arguments) click to toggle source
# File lib/sqreen/js/mini_racer_executable_js.rb, line 39
def run_js_cb(cb_name, budget, arguments)
  Sqreen.log.debug { "js:#{self.class} callback:#{cb_name} pool:#{@pool.inspect}" }
  @pool.with_context do |ctx|
    Sqreen.log.debug { "js:#{self.class} callback:#{cb_name} context:#{ctx.inspect}" }
    if ctx.code_failed?(@code_id)
      Sqreen.log.debug do
        "Skipping execution of callback #{cb_name} (code md5 #{@code_id})" \
        " due to prev failure of definition evaluation"
      end
      return nil
    end

    ctx.add_code(@code_id, @code) unless ctx.code?(@code_id)

    # mini_racer expects timeout to be in ms
    ctx.timeout = budget ? budget * 1000.0 : nil
    begin
      ctx.call("sqreen_#{@code_id}_#{cb_name}", *arguments)
    rescue @module::ScriptTerminatedError
      Sqreen.log.debug "ScriptTerminatedError/#{cb_name}"
      nil
    end
  end
end