Class: JSObfu::Hoister

Inherits:
RKelly::Visitors::Visitor
  • Object
show all
Defined in:
lib/jsobfu/hoister.rb

Overview

Walks a Javascript AST and finds the immediate members of the root scope, which is useful for “hoisting” var and function declaration to the top of the function.

Although the auto-hoisting is no longer used, this class is used to “discover” a function’s variables and scope.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Hoister

Returns a new instance of Hoister.

Parameters:

  • opts (Hash) (defaults to: {})

    the options hash

Options Hash (opts):

  • :max_depth (Integer)

    the maximum depth to hoist (1)

  • :parent_scope (Scope)

    the owner’s scope



20
21
22
23
24
25
26
27
# File 'lib/jsobfu/hoister.rb', line 20

def initialize(opts={})
  @parent_scope = opts.fetch(:parent_scope, nil)
  @max_depth  = 1
  @depth      = 0
  @scope      = {}
  @functions  = []
  super()
end

Instance Attribute Details

#functionsArray<String> (readonly)

Returns the function names in the first level of this closure.

Returns:

  • (Array<String>)

    the function names in the first level of this closure



15
16
17
# File 'lib/jsobfu/hoister.rb', line 15

def functions
  @functions
end

#scopeHash (readonly)

Returns the scope maintained while walking the ast.

Returns:

  • (Hash)

    the scope maintained while walking the ast



12
13
14
# File 'lib/jsobfu/hoister.rb', line 12

def scope
  @scope
end

Instance Method Details

#scope_declaration(opts = {}) ⇒ String

Returns Javascript that declares the discovered variables.

Returns:

  • (String)

    Javascript that declares the discovered variables



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/jsobfu/hoister.rb', line 71

def scope_declaration(opts={})
  keys = scope.keys.dup
  if opts.fetch(:shuffle, true)
    keys = keys.shuffle
  end

  keys.delete_if { |k| functions.include? k }

  if @parent_scope
    keys.delete_if { |k| @parent_scope.has_key? k }
    keys.map! { |k| @parent_scope.renames[k.to_s] || k }
  end

  if keys.empty? then '' else "var #{keys.join(",")};" end
end

#visit_FunctionDeclNode(o) ⇒ Object



40
41
42
43
# File 'lib/jsobfu/hoister.rb', line 40

def visit_FunctionDeclNode(o)    
  functions << o.value
  scope[o.value] = o
end

#visit_SourceElementsNode(o) ⇒ Object



29
30
31
32
33
34
# File 'lib/jsobfu/hoister.rb', line 29

def visit_SourceElementsNode(o)
  return if @max_depth and @depth >= @max_depth
  @depth += 1
  o.value.each { |x| x.accept(self) }
  @depth -= 1
end

#visit_VarDeclNode(o) ⇒ Object



36
37
38
# File 'lib/jsobfu/hoister.rb', line 36

def visit_VarDeclNode(o)
  scope[o.name] = o
end