summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreyChudnov <>2013-06-23 03:16:49 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2013-06-23 03:16:49 (GMT)
commite068d591fc6df3d82090d2b1fe2e08909663033b (patch)
tree7f70b40b23aa0b8bec6000047a63572db9ea2be9
version 0.90.9
-rw-r--r--LICENSE28
-rw-r--r--Setup.hs2
-rw-r--r--language-ecmascript-analysis.cabal61
-rw-r--r--src/Language/ECMAScript3/Analysis/LabelSet.hs88
-rw-r--r--src/Language/ECMAScript3/Analysis/LexicalEnvironment.hs157
-rw-r--r--test/TestMain.hs3
6 files changed, 339 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ac6e4ee
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2007--2012, Brown University, 2008-2012 Claudiu Saftoiu,
+2012 Stevens Institute of Technology.
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Brown University, Stevens Institute of Technology
+ nor the names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/language-ecmascript-analysis.cabal b/language-ecmascript-analysis.cabal
new file mode 100644
index 0000000..232e5c8
--- /dev/null
+++ b/language-ecmascript-analysis.cabal
@@ -0,0 +1,61 @@
+-- Initial language-ecmascript-analysis.cabal generated by cabal init. For
+-- further documentation, see http://haskell.org/cabal/users-guide/
+
+name: language-ecmascript-analysis
+version: 0.9
+cabal-version: >= 1.10
+copyright: (c) 2007-2012 Brown University, (c) 2008-2010 Claudiu Saftoiu,
+ (c) 2012-2013 Stevens Institute of Technology
+synopsis: JavaScript analysis library
+description: Includes a label set analysis and a lexical environment analysis
+license: BSD3
+license-file: LICENSE
+author: Andrey Chudnov, Arjun Guha, Spiridon Aristides Eliopoulos,
+ Joe Gibbs Politz, Claudiu Saftoiu
+maintainer: Andrey Chudnov <oss@chudnov.com>
+homepage: http://github.com/jswebtools/language-ecmascript-analysis
+bug-reports: http://github.com/jswebtools/language-ecmascript-analysis/issues
+category: Language
+build-type: Simple
+stability: experimental
+Tested-with: GHC==7.6.3
+
+source-repository head
+ type: git
+ location: git://github.com/jswebtools/language-ecmascript-analysis.git
+
+source-repository this
+ type: git
+ location: git://github.com/jswebtools/language-ecmascript-analysis.git
+ tag: 0.9
+
+Library
+ Build-Depends: base >= 4 && < 5
+ , language-ecmascript >= 0.15 && < 1.0
+ , uniplate >= 1.6 && < 1.7
+ , parsec >= 3 && < 4
+ , containers >= 0.1 && < 0.6
+ hs-source-dirs:
+ src
+ exposed-modules:
+ Language.ECMAScript3.Analysis.LexicalEnvironment
+ Language.ECMAScript3.Analysis.LabelSet
+ Default-Extensions: DeriveDataTypeable, ScopedTypeVariables, DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleContexts
+ ghc-options:
+ -fwarn-incomplete-patterns
+ default-language: Haskell2010
+
+Test-Suite test
+ hs-source-dirs: test
+ Type: exitcode-stdio-1.0
+ Main-Is: TestMain.hs
+ Build-Depends: base >= 4 && < 5,
+ uniplate >= 1.6 && <1.7,
+ language-ecmascript >= 0.15 && < 1.0,
+ HUnit,
+ test-framework >= 0.8 && < 0.9,
+ test-framework-hunit >= 0.3.0 && < 0.4,
+ mtl
+ Default-Extensions: DeriveDataTypeable, ScopedTypeVariables, DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleContexts
+ Default-Language: Haskell2010
+ ghc-options: -fwarn-incomplete-patterns
diff --git a/src/Language/ECMAScript3/Analysis/LabelSet.hs b/src/Language/ECMAScript3/Analysis/LabelSet.hs
new file mode 100644
index 0000000..a7b437a
--- /dev/null
+++ b/src/Language/ECMAScript3/Analysis/LabelSet.hs
@@ -0,0 +1,88 @@
+-- | Label-set analysis which annotates all the statements in the script
+-- with their label sets according to ECMAScript specification,
+-- section 12.12. The result of this analysis are useful for building
+-- control-flow graphs.
+
+module Language.ECMAScript3.Analysis.LabelSet (annotateLabelSets
+ ,Label(..)) where
+
+import Language.ECMAScript3.Syntax
+import Language.ECMAScript3.Syntax.Annotations
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Generics.Uniplate.Data
+import Data.Data (Data)
+import Control.Applicative
+import Data.Typeable (Typeable)
+
+-- | Labels are either strings (identifiers) or /empty/ (see 12.12 of
+-- the spec)
+data Label = Label String
+ | EmptyLabel
+ deriving (Ord, Eq, Show, Data, Typeable)
+
+-- | Annotates statements with their label sets; example use:
+--
+-- >>> let jsa = reannotate (\a -> (a, Set.empty))
+-- >>> in annotateLabelSets jsa snd (\labs (a, ls) -> (a, labs `Set.union` ls))
+annotateLabelSets :: Data a =>
+ (a -> Set Label) -- ^ annotation read function
+ -> (Set Label -> a -> a) -- ^ annotation write function
+ -> JavaScript a -- ^ the script to annotate
+ -> JavaScript a
+annotateLabelSets r w = transformBi (annotateFuncStmtBodies r w)
+ . transformBi (annotateFuncExprBodies r w)
+ . descendBi (annotateStatement r w)
+
+annotateFuncStmtBodies :: Data a =>
+ (a -> Set Label)
+ -> (Set Label -> a -> a)
+ -> Statement a
+ -> Statement a
+annotateFuncStmtBodies r w s = case s of
+ FunctionStmt a name params body ->
+ let newbody = map (descend (annotateStatement r w)) body
+ in FunctionStmt a name params newbody
+ _ -> s
+
+annotateFuncExprBodies :: Data a =>
+ (a -> Set Label)
+ -> (Set Label -> a -> a)
+ -> Expression a
+ -> Expression a
+annotateFuncExprBodies r w e = case e of
+ FuncExpr a mname params body ->
+ let newbody = map (descend (annotateStatement r w)) body
+ in FuncExpr a mname params newbody
+ _ -> e
+
+-- | 12.12 ECMA262: the production /Identifier/ : /Statement/ is
+-- evaluated by adding /Identifier/ to the label ser of /Statement/
+-- and then evluating /Statement/. If the /LabelledStatement/ itsef
+-- has a non-empty label set, these labels are also added to the label
+-- set of /Statement/ before evaluating it. ... Prior to evaluation of
+-- a /LabelledStatement/, the contained /Statement/ is regarded as
+-- possessing an empty label set, unless it is an /IterationStatement/
+-- or a /SwitchStatement/, in which case it is regarded as possessing
+-- a label set consisting of the single element, @empty@.
+annotateStatement :: Data a =>
+ (a -> Set Label)
+ -> (Set Label -> a -> a)
+ -> Statement a
+ -> Statement a
+annotateStatement r w s = case s of
+ LabelledStmt ann lab stmt ->
+ let labelset = Set.insert (id2Label lab) (r ann)
+ newstmt = annotateStatement r w $ w labelset <$> stmt
+ in LabelledStmt ann lab newstmt
+ SwitchStmt {} ->
+ let labelset = Set.insert EmptyLabel (r $ getAnnotation s)
+ in descend (annotateStatement r w) (w labelset <$> s)
+ _ | isIterationStmt s ->
+ let labelset = Set.insert EmptyLabel (r $ getAnnotation s)
+ in descend (annotateStatement r w) (w labelset <$> s)
+ _ -> descend (annotateStatement r w) s
+
+id2Label :: Id a -> Label
+id2Label = Label . unId
+
diff --git a/src/Language/ECMAScript3/Analysis/LexicalEnvironment.hs b/src/Language/ECMAScript3/Analysis/LexicalEnvironment.hs
new file mode 100644
index 0000000..2ce5b08
--- /dev/null
+++ b/src/Language/ECMAScript3/Analysis/LexicalEnvironment.hs
@@ -0,0 +1,157 @@
+-- | A lexical environment analysis of ECMAScript programs
+
+module Language.ECMAScript3.Analysis.LexicalEnvironment
+ ( env
+ , localVars
+ , EnvTree (..)
+ ) where
+
+import Data.List
+import Data.Maybe
+import qualified Data.Map as M
+import Data.Map (Map)
+import qualified Data.Set as S
+import Data.Set (Set)
+import Text.ParserCombinators.Parsec.Pos (SourcePos)
+
+import Language.ECMAScript3.Syntax
+
+-- | Intermediate data structure that contains locally declared names and
+-- all references to identifers.
+data Partial = Partial {
+ partialLocals :: M.Map String SourcePos,
+ partialReferences :: M.Map String SourcePos,
+ partialNested :: [Partial]
+}
+
+empty :: Partial
+empty = Partial M.empty M.empty []
+
+ref :: Id SourcePos -> Partial
+ref (Id p v) = Partial M.empty (M.singleton v p) []
+
+decl :: Id SourcePos -> Partial
+decl (Id p v) = Partial (M.singleton v p) M.empty []
+
+nest :: Partial -> Partial
+nest partial = Partial M.empty M.empty [partial]
+
+-- Combine partial results from the same lexical scope.
+unions :: [Partial] -> Partial
+unions ps = Partial (M.unions (map partialLocals ps))
+ (M.unions (map partialReferences ps))
+ (concatMap partialNested ps)
+
+javascript :: JavaScript SourcePos -> Partial
+javascript (Script _ ss) = unions (map stmt ss)
+
+
+lvalue :: LValue SourcePos -> Partial
+lvalue lv = case lv of
+ LVar p x -> ref (Id p x)
+ LDot _ e _ -> expr e
+ LBracket _ e1 e2 -> unions [expr e1, expr e2]
+
+expr :: Expression SourcePos -> Partial
+expr e = case e of
+ StringLit _ _ -> empty
+ RegexpLit {} -> empty
+ NumLit _ _ -> empty
+ IntLit _ _ -> empty
+ BoolLit _ _ -> empty
+ NullLit _ -> empty
+ ArrayLit _ es -> unions (map expr es)
+ ObjectLit _ props -> unions (map (expr.snd) props)
+ ThisRef _ -> empty
+ VarRef _ id -> empty
+ DotRef _ e _ -> expr e
+ BracketRef _ e1 e2 -> unions [expr e1, expr e2]
+ NewExpr _ e1 es -> unions [expr e1, unions $ map expr es]
+ PrefixExpr _ _ e -> expr e
+ InfixExpr _ _ e1 e2 -> unions [expr e1, expr e2]
+ CondExpr _ e1 e2 e3 -> unions [expr e1, expr e2, expr e3]
+ AssignExpr _ _ lv e -> unions [lvalue lv, expr e]
+ UnaryAssignExpr _ _ lv -> lvalue lv
+ ListExpr _ es -> unions (map expr es)
+ CallExpr _ e es -> unions [expr e, unions $ map expr es]
+ FuncExpr _ _ args ss -> nest $ unions [unions $ map decl args
+ ,unions $ map stmt ss]
+
+caseClause :: CaseClause SourcePos -> Partial
+caseClause cc = case cc of
+ CaseClause _ e ss -> unions [expr e, unions $ map stmt ss]
+ CaseDefault _ ss -> unions $ map stmt ss
+
+-- TODO: Verify that this is a declaration and not a reference.
+catchClause :: CatchClause SourcePos -> Partial
+catchClause (CatchClause _ id s) = unions [decl id, stmt s]
+
+varDecl :: VarDecl SourcePos -> Partial
+varDecl (VarDecl _ id Nothing) = decl id
+varDecl (VarDecl _ id (Just e)) = unions [decl id, expr e]
+
+forInit :: ForInit SourcePos -> Partial
+forInit fi = case fi of
+ NoInit -> empty
+ VarInit ds -> unions $ map varDecl ds
+ ExprInit e -> expr e
+
+forInInit :: ForInInit SourcePos -> Partial
+forInInit (ForInVar id) = decl id
+forInInit (ForInLVal lv) = lvalue lv
+
+stmt :: Statement SourcePos -> Partial
+stmt s = case s of
+ BlockStmt _ ss -> unions $ map stmt ss
+ EmptyStmt _ -> empty
+ ExprStmt _ e -> expr e
+ IfStmt _ e s1 s2 -> unions [expr e, stmt s1, stmt s2]
+ IfSingleStmt _ e s -> unions [expr e, stmt s]
+ SwitchStmt _ e cases -> unions [expr e, unions $ map caseClause cases]
+ WhileStmt _ e s -> unions [expr e, stmt s]
+ DoWhileStmt _ s e -> unions [stmt s, expr e]
+ BreakStmt _ _ -> empty
+ ContinueStmt _ _ -> empty
+ LabelledStmt _ _ s -> stmt s
+ ForInStmt _ fii e s -> unions [forInInit fii, expr e, stmt s]
+ ForStmt _ fi me1 me2 s ->
+ unions [forInit fi, maybe empty expr me1, maybe empty expr me2, stmt s]
+ TryStmt _ s mcatch ms ->
+ unions [stmt s, maybe empty catchClause mcatch, maybe empty stmt ms]
+ ThrowStmt _ e -> expr e
+ ReturnStmt _ me -> maybe empty expr me
+ WithStmt _ e s -> unions [expr e, stmt s]
+ VarDeclStmt _ decls -> unions $ map varDecl decls
+ FunctionStmt _ fnId args ss ->
+ unions [decl fnId, nest $ unions [unions $ map decl args,
+ unions $ map stmt ss]]
+
+-- |The statically-determinate lexical structure of a JavaScript program.
+data EnvTree = EnvTree (M.Map String SourcePos) [EnvTree]
+
+-- A 'Partial' specifies identifier references in addition to identifier
+-- declarations. We descend into a 'Partial', pushing enclosing declarations
+-- in to remove references to identifiers declared in the enclosing scope.
+-- Any referencs to identifiers not declared in either the current or the
+-- enclosing scope are local definitions of global variables.
+makeEnvTree :: Map String SourcePos -- ^enclosing environment
+ -> Partial -- ^local environment and references
+ -> (EnvTree,Map String SourcePos)
+ -- ^environment and global definitions
+makeEnvTree enclosing (Partial locals references nested) = (tree,globals) where
+ nestedResults = map (makeEnvTree (locals `M.union` enclosing)) nested
+ tree = EnvTree locals (map fst nestedResults)
+ globals' = (references `M.difference` locals) `M.difference` enclosing
+ globals = M.unions (globals':map snd nestedResults)
+
+env :: Map String SourcePos -- ^browser/testing environment
+ -> [Statement SourcePos]
+ -> (EnvTree,Map String SourcePos)
+env globals program = makeEnvTree globals (unions $ map stmt program)
+
+
+localVars :: [Statement SourcePos]
+ -> [(String, SourcePos)]
+localVars body = M.toList locals where
+ Partial locals _ _ = unions $ map stmt body
+
diff --git a/test/TestMain.hs b/test/TestMain.hs
new file mode 100644
index 0000000..de106fe
--- /dev/null
+++ b/test/TestMain.hs
@@ -0,0 +1,3 @@
+module Main where
+
+main = return ()