Commit 469276bb authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Use AstNode inside Ast#detect method.

Eliminating some Ast class methods that are now taken care of by AstNode class.
parent 48bb3d21
Loading
Loading
Loading
Loading
+44 −71
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ require "jsduck/serializer"
require "jsduck/evaluator"
require "jsduck/function_ast"
require "jsduck/ext_patterns"
require "jsduck/ast_node"

module JsDuck

@@ -47,91 +48,91 @@ module JsDuck
    #
    #     { :tagname => :method, :name => "foo", ... }
    #
    def detect(ast)
      ast = ast || {}
    def detect(node)
      ast = AstNode.new(node)

      exp = expression?(ast) ? ast["expression"] : nil
      var = var?(ast) ? ast["declarations"][0] : nil
      exp = ast.expression_statement? ? ast["expression"] : nil
      var = ast.variable_declaration? ? ast["declarations"][0] : nil

      # Ext.define("Class", {})
      if exp && ext_define?(exp)
        make_class(to_value(exp["arguments"][0]), exp)
      if exp && exp.ext_define?
        make_class(exp["arguments"][0].to_value, exp.raw)

      # Ext.override(Class, {})
      elsif exp && ext_override?(exp)
        make_class("", exp)
      elsif exp && exp.ext_override?
        make_class("", exp.raw)

      # foo = Ext.extend(Parent, {})
      elsif exp && assignment?(exp) && ext_extend?(exp["right"])
        make_class(to_s(exp["left"]), exp["right"])
      elsif exp && exp.assignment_expression? && exp["right"].ext_extend?
        make_class(exp["left"].to_s, exp["right"].raw)

      # Foo = ...
      elsif exp && assignment?(exp) && class_name?(to_s(exp["left"]))
        make_class(to_s(exp["left"]), exp["right"])
      elsif exp && exp.assignment_expression? && class_name?(exp["left"].to_s)
        make_class(exp["left"].to_s, exp["right"].raw)

      # var foo = Ext.extend(Parent, {})
      elsif var && var["init"] && ext_extend?(var["init"])
        make_class(to_s(var["id"]), var["init"])
      elsif var && var["init"].ext_extend?
        make_class(var["id"].to_s, var["init"].raw)

      # var Foo = ...
      elsif var && class_name?(to_s(var["id"]))
        make_class(to_s(var["id"]), var["right"])
      elsif var && class_name?(var["id"].to_s)
        make_class(var["id"].to_s, var["right"].raw)

      # function Foo() {}
      elsif function?(ast) && class_name?(to_s(ast["id"]))
        make_class(to_s(ast["id"]))
      elsif ast.function? && class_name?(ast["id"].to_s)
        make_class(ast["id"].to_s)

      # { ... }
      elsif object?(ast)
        make_class("", ast)
      elsif ast.object_expression?
        make_class("", ast.raw)

      # function foo() {}
      elsif function?(ast)
        make_method(to_s(ast["id"]), ast)
      elsif ast.function?
        make_method(ast["id"].to_s, ast.raw)

      # foo = function() {}
      elsif exp && assignment?(exp) && function?(exp["right"])
        make_method(to_s(exp["left"]), exp["right"])
      elsif exp && exp.assignment_expression? && exp["right"].function?
        make_method(exp["left"].to_s, exp["right"].raw)

      # var foo = function() {}
      elsif var && var["init"] && function?(var["init"])
        make_method(to_s(var["id"]), var["init"])
      elsif var && var["init"] && var["init"].function?
        make_method(var["id"].to_s, var["init"].raw)

      # (function() {})
      elsif exp && function?(exp)
        make_method(exp["id"] ? to_s(exp["id"]) : "", exp)
      elsif exp && exp.function?
        make_method(exp["id"].to_s || "", exp.raw)

      # foo: function() {}
      elsif property?(ast) && function?(ast["value"])
        make_method(key_value(ast["key"]), ast["value"])
      elsif ast.property? && ast["value"].function?
        make_method(key_value(ast["key"].raw), ast["value"].raw)

      # this.fireEvent("foo", ...)
      elsif exp && fire_event?(exp)
        make_event(to_value(exp["arguments"][0]))
      elsif exp && exp.fire_event?
        make_event(exp["arguments"][0].to_value)

      # foo = ...
      elsif exp && assignment?(exp)
        make_property(to_s(exp["left"]), exp["right"])
      elsif exp && exp.assignment_expression?
        make_property(exp["left"].to_s, exp["right"].raw)

      # var foo = ...
      elsif var
        make_property(to_s(var["id"]), var["init"])
        make_property(var["id"].to_s, var["init"].raw)

      # foo: ...
      elsif property?(ast)
        make_property(key_value(ast["key"]), ast["value"])
      elsif ast.property?
        make_property(key_value(ast["key"].raw), ast["value"].raw)

      # foo;
      elsif exp && ident?(exp)
        make_property(to_s(exp))
      elsif exp && exp.identifier?
        make_property(exp.to_s)

      # "foo"  (inside some expression)
      elsif string?(ast)
        make_property(to_value(ast))
      elsif ast.string?
        make_property(ast.to_value)

      # "foo";  (as a statement of it's own)
      elsif exp && string?(exp)
        make_property(to_value(exp))
      elsif exp && exp.string?
        make_property(exp.to_value)

      else
        make_property()
@@ -140,18 +141,10 @@ module JsDuck

    private

    def expression?(ast)
      ast["type"] == "ExpressionStatement"
    end

    def call?(ast)
      ast["type"] == "CallExpression"
    end

    def assignment?(ast)
      ast["type"] == "AssignmentExpression"
    end

    def ext_define?(ast)
      call?(ast) && ext_pattern?("Ext.define", ast["callee"])
    end
@@ -176,26 +169,6 @@ module JsDuck
      ExtPatterns.matches?(pattern, to_s(ast))
    end

    def fire_event?(ast)
      call?(ast) && to_s(ast["callee"]) == "this.fireEvent"
    end

    def var?(ast)
      ast["type"] == "VariableDeclaration"
    end

    def property?(ast)
      ast["type"] == "Property"
    end

    def ident?(ast)
      ast["type"] == "Identifier"
    end

    def string?(ast)
      ast["type"] == "Literal" && ast["value"].is_a?(String)
    end

    def object?(ast)
      ast["type"] == "ObjectExpression"
    end
+101 −50
Original line number Diff line number Diff line
@@ -9,65 +9,30 @@ module JsDuck
  class AstNode
    # Initialized with a AST Hash from Esprima.
    def initialize(node)
      @node = node
    end

    def expression?
      @node["type"] == "ExpressionStatement"
    end

    def call?
      @node["type"] == "CallExpression"
    end

    def assignment?(ast)
      @node["type"] == "AssignmentExpression"
    end

    def function?(ast)
      @node["type"] == "FunctionDeclaration" || @node["type"] == "FunctionExpression" || empty_fn?
    end

    def empty_fn?(ast)
      @node["type"] == "MemberExpression" && ext_pattern?("Ext.emptyFn")
    end

    def ext_pattern?(pattern)
      @ext_patterns.matches?(pattern, to_s)
    end

    def fire_event?(ast)
      call? && child("callee").to_s == "this.fireEvent"
    end

    def var?(ast)
      @node["type"] == "VariableDeclaration"
    end

    def property?(ast)
      @node["type"] == "Property"
    end

    def ident?(ast)
      @node["type"] == "Identifier"
    end

    def string?(ast)
      @node["type"] == "Literal" && @node["value"].is_a?(String)
    end

    def object?(ast)
      @node["type"] == "ObjectExpression"
      @node = node || {}
    end

    # Returns a child AST node as AstNode class.
    def child(name)
      AstNode.new(@node[name])
    end
    # Shorthand for #child method
    def [](name)
      child(name)
    end

    # Returns the raw Exprima AST node this class wraps.
    def raw
      @node
    end

    # Serializes the node into string
    def to_s
      begin
        Serializer.new.to_s(@node)
      rescue
        nil
      end
    end

    # Evaluates the node into basic JavaScript value.
@@ -79,6 +44,7 @@ module JsDuck
      end
    end

    # Returns the type of node value.
    def value_type
      v = to_value
      if v.is_a?(String)
@@ -98,6 +64,91 @@ module JsDuck
      end
    end

    # Tests for higher level types which don't correspond directly to
    # Esprima AST types.

    def function?
      function_declaration? || function_expression? || ext_empty_fn?
    end

    def fire_event?
      call_expression? && child("callee").to_s == "this.fireEvent"
    end

    def string?
      literal? && @node["value"].is_a?(String)
    end

    # Checks dependent on Ext namespace,
    # which may not always be "Ext" but also something user-defined.

    def ext_empty_fn?
      member_expression? && ext_pattern?("Ext.emptyFn")
    end

    def ext_define?
      call_expression? && child("callee").ext_pattern?("Ext.define")
    end

    def ext_extend?
      call_expression? && child("callee").ext_pattern?("Ext.extend")
    end

    def ext_override?
      call_expression? && child("callee").ext_pattern?("Ext.override")
    end

    def ext_pattern?(pattern)
      ExtPatterns.matches?(pattern, to_s)
    end

    # Simple shorthands for testing the type of node
    # These have one-to-one mapping to Esprima node types.

    def call_expression?
      @node["type"] == "CallExpression"
    end

    def assignment_expression?
      @node["type"] == "AssignmentExpression"
    end

    def object_expression?
      @node["type"] == "ObjectExpression"
    end

    def function_expression?
      @node["type"] == "FunctionExpression"
    end

    def member_expression?
      @node["type"] == "MemberExpression"
    end

    def expression_statement?
      @node["type"] == "ExpressionStatement"
    end

    def variable_declaration?
      @node["type"] == "VariableDeclaration"
    end

    def function_declaration?
      @node["type"] == "FunctionDeclaration"
    end

    def property?
      @node["type"] == "Property"
    end

    def identifier?
      @node["type"] == "Identifier"
    end

    def literal?
      @node["type"] == "Literal"
    end

  end

end
+1 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ module JsDuck
        ast["raw"]

      else
        throw "Unknown node type: "+ast["type"]
        throw "Unknown node type: " + (ast["type"] || "nil")
      end
    end