Class: Sqreen::Js::MiniRacerExecutableJs

Inherits:
ExecutableJs show all
Defined in:
lib/sqreen/js/mini_racer_executable_js.rb

Constant Summary collapse

@@ctx_defined =

rubocop:disable Style/ClassVars

false

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pool, code, vendored) ⇒ MiniRacerExecutableJs

Returns a new instance of MiniRacerExecutableJs.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 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

Class Method Details

.code_id(code) ⇒ Object



64
65
66
# 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) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# 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 if respond_to?(: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

Instance Method Details

#ctx_defined?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/sqreen/js/mini_racer_executable_js.rb', line 16

def ctx_defined?
  @@ctx_defined
end

#define_ctx!Object



20
21
22
# 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) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 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