Skip to content

Commit

Permalink
Use different analysis framework
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnaldo committed Oct 1, 2024
1 parent ada853c commit e279372
Show file tree
Hide file tree
Showing 29 changed files with 2,903 additions and 161 deletions.
9 changes: 2 additions & 7 deletions .completion
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,19 @@ _esmeta_completions() {
local cur prev opts lastc informats outformats datafiles
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
cmdList="help extract compile build-cfg tycheck parse eval web test262-test inject mutate analyze"
cmdList="help extract compile build-cfg tycheck parse eval web test262-test inject mutate"
globalOpt="-silent -error -status -time -test262dir"
helpOpt=""
extractOpt="-extract:target -extract:log -extract:eval -extract:repl"
compileOpt="-compile:log -compile:log-with-loc"
buildcfgOpt="-build-cfg:log -build-cfg:dot -build-cfg:pdf"
tycheckOpt="-tycheck:target -tycheck:repl -tycheck:repl-continue -tycheck:ignore -tycheck:update-ignore -tycheck:log -tycheck:detail-log -tycheck:type-sens -tycheck:type-guard"
tycheckOpt="-tycheck:target -tycheck:repl -tycheck:repl-continue -tycheck:ignore -tycheck:update-ignore -tycheck:log -tycheck:detail-log -tycheck:type-guard"
parseOpt="-parse:debug"
evalOpt="-eval:timeout -eval:multiple -eval:log -eval:detail-log"
webOpt="-web:port"
test262testOpt="-test262-test:target -test262-test:features -test262-test:progress -test262-test:coverage -test262-test:timeout -test262-test:with-yet -test262-test:log -test262-test:detail-log -test262-test:concurrent"
injectOpt="-inject:defs -inject:out -inject:log"
mutateOpt="-mutate:out -mutate:mutator -mutate:untilValid"
analyzeOpt="-analyze:repl"
# completion for commands
case "${COMP_CWORD}" in
1)
Expand Down Expand Up @@ -81,10 +80,6 @@ _esmeta_completions() {
COMPREPLY=($(compgen -W "${globalOpt} ${extractOpt} ${compileOpt} ${buildcfgOpt} ${mutateOpt}"))
return 0
;;
analyze)
COMPREPLY=($(compgen -W "${globalOpt} ${extractOpt} ${compileOpt} ${buildcfgOpt} ${analyzeOpt}"))
return 0
;;
esac
return 0
;;
Expand Down
9 changes: 9 additions & 0 deletions src/main/resources/manuals/types
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,15 @@ type DataView extends OrdinaryObject {
ByteOffset: NonNegInt;
}

// TODO https://tc39.es/ecma262/#table-json-serialization-record
// type JSONSerializationRecord {
// ReplacerFunction: Record[FunctionObject] | Undefined;
// PropertyList: List[String] | Undefined;
// Gap: String;
// Stack: List[Record[Object]];
// Indent: String;
// }

// https://tc39.es/ecma262/#sec-properties-of-weak-ref-instances
type WeakRef extends OrdinaryObject {
WeakRefTarget: Record[Object | Symbol];
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/esmeta/Command.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ case object CmdBuildCFG extends Command("build-cfg", CmdCompile >> BuildCFG) {
// Analysis of ECMA-262
// -----------------------------------------------------------------------------
/** `tycheck` command */
case object CmdTypeCheck extends Command("tycheck", CmdBuildCFG >> TypeCheck) {
val help = "performs a type analysis of ECMA-262."
case object CmdTyCheck extends Command("tycheck", CmdBuildCFG >> TyCheck) {
val help = "performs a type checking of ECMA-262."
val examples = List(
"esmeta tycheck # type check for spec.",
"esmeta tycheck -tycheck:target='.*ToString' # type check with targets",
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/esmeta/ESMeta.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ object ESMeta extends Git(BASE_DIR) {
CmdCompile,
CmdBuildCFG,
// Analysis of ECMA-262
CmdTypeCheck,
CmdTyCheck,
// Interpreter & Double Debugger for ECMAScript
CmdParse,
CmdEval,
Expand All @@ -91,7 +91,7 @@ object ESMeta extends Git(BASE_DIR) {
Compile,
BuildCFG,
// Analysis of ECMA-262
TypeCheck,
TyCheck,
// Interpreter & Double Debugger for ECMAScript
Parse,
Eval,
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/esmeta/analyzer/AbsTransferLike.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ trait AbsTransferLikeDecl { self: Analyzer =>
/** transfer function for node points */
def apply(np: NodePoint[_]): Unit

/** transfer function for return points */
def apply(rp: ReturnPoint): Unit

/** transfer function for normal instructions */
def transfer(
inst: NormalInst,
Expand Down
80 changes: 51 additions & 29 deletions src/main/scala/esmeta/analyzer/Analyzer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import esmeta.error.NotSupported.given
import esmeta.es.Ast
import esmeta.ir.{Func => _, util => _, *}
import esmeta.util.*
import esmeta.util.Appender.*
import esmeta.util.BaseUtils.*

/** static analyzer */
abstract class Analyzer
extends AbsTransferLikeDecl
with AnalysisPointDecl
with ControlPointDecl
with DomainLikeDecl
with TypeErrorDecl
with ViewLikeDecl
with util.Decl
with repl.Decl {
Expand All @@ -24,22 +24,16 @@ abstract class Analyzer
/** control flow graph */
val cfg: CFG

/** worklist of control points */
val worklist: Worklist[ControlPoint]

/** view abstraction for analysis sensitivities */
type View <: ViewLike

/** abstract transfer function */
type AbsTransfer <: AbsTransferLike
val transfer: AbsTransfer

/** empty view */
val emptyView: View

/** appender rule for views */
given viewRule: Rule[View]

/** get entry views of loops */
def getEntryView(view: View): View

/** check reachability of node points */
def reachable(np: NodePoint[Node]): Boolean

Expand All @@ -56,17 +50,12 @@ abstract class Analyzer
type AbsValue <: AbsValueLike

/** lookup for node points */
def getResult(np: NodePoint[Node]): AbsState
def getResult(np: NodePoint[Node]): AbsState =
npMap.getOrElse(np, AbsState.Bot)

/** lookup for return points */
def getResult(rp: ReturnPoint): AbsRet

/** get string for result of control points */
def getString(
cp: ControlPoint,
color: String,
detail: Boolean,
): String = getString(cp, Some(color), detail)
def getResult(rp: ReturnPoint): AbsRet =
rpMap.getOrElse(rp, AbsRet.Bot)

/** get string for result of control points */
def getString(
Expand All @@ -75,11 +64,13 @@ abstract class Analyzer
detail: Boolean = false,
): String

val log: Boolean

/** logging the current analysis result */
def logging: Unit

// ---------------------------------------------------------------------------
// Mutable Analysis Status
// Analysis Status
// ---------------------------------------------------------------------------
/** abstract states in each node point */
var npMap: Map[NodePoint[Node], AbsState] = Map()
Expand All @@ -102,26 +93,23 @@ abstract class Analyzer
/** count for each control point */
var counter: Map[ControlPoint, Int] = Map()

/** worklist of control points */
var worklist: Worklist[ControlPoint]

/** set start time of analyzer */
var startTime: Long = System.currentTimeMillis

/** analysis time limit */
var timeLimit: Option[Long] = None
val timeLimit: Option[Long] = None

/** debugging mode */
var debugMode: Boolean = false
val debugMode: Boolean = false

/** REPL mode */
var useRepl: Boolean = false
val useRepl: Boolean = false

/** Run continue command at startup when using repl */
var replContinue: Boolean = false
val replContinue: Boolean = false

/** check period */
var checkPeriod: Int = 10000
val checkPeriod: Int = 10000

/** throw exception for not yet compiled expressions */
val yetThrow: Boolean = false
Expand All @@ -131,6 +119,11 @@ abstract class Analyzer
// ---------------------------------------------------------------------------
given CFG = cfg

/** perform type analysis */
lazy val analyze: Unit =
transfer.fixpoint
if (log) logging

/** analyzer elements */
trait AnalyzerElem {
override def toString: String = toString(false, false, false)
Expand Down Expand Up @@ -167,6 +160,35 @@ abstract class Analyzer
def count(cp: ControlPoint): Unit =
counter += cp -> (counter.getOrElse(cp, 0) + 1)

/** get elapsed time of analyzer */
def elapsedTime: Long = System.currentTimeMillis - startTime

/** set start time of analyzer */
def allCPs: Set[ControlPoint] = npMap.keySet ++ rpMap.keySet

/** set of analyzed functions */
def analyzedFuncs: Set[Func] = npMap.keySet.map(_.func) ++ analyzedReturns

/** set of analyzed nodes */
def analyzedNodes: Set[Node] = npMap.keySet.map(_.node)

/** set of analyzed function returns */
def analyzedReturns: Set[Func] = rpMap.keySet.map(_.func)

/** get string for result of all control points */
def getStrings: List[String] = getStrings(None, false)
def getStrings(
color: Option[String] = None,
detail: Boolean = false,
): List[String] = allCPs.toList.sorted.map(getString(_, color, detail))

/** get string for result of control points */
def getString(
cp: ControlPoint,
color: String,
detail: Boolean,
): String = getString(cp, Some(color), detail)

/** exploded */
def exploded(msg: String): Nothing = throw AnalysisImprecise(msg)

Expand Down
35 changes: 35 additions & 0 deletions src/main/scala/esmeta/analyzer/ControlPoint.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package esmeta.analyzer

import esmeta.cfg.*
import esmeta.ir.{Func => _, *}

trait ControlPointDecl { self: Analyzer =>

/** control points */
sealed trait ControlPoint extends AnalyzerElem {
def view: View
def func: Func
def isBuiltin: Boolean = func.isBuiltin
def toReturnPoint: ReturnPoint = this match
case np: NodePoint[Node] => ReturnPoint(np.func, np.view)
case rp: ReturnPoint => rp
}

/** node points */
case class NodePoint[+T <: Node](
func: Func,
node: T,
view: View,
) extends ControlPoint

/** return points */
case class ReturnPoint(
func: Func,
view: View,
) extends ControlPoint

given Ordering[ControlPoint] = Ordering.by(_ match
case NodePoint(f, n, _) => (f.id, n.id)
case ReturnPoint(f, _) => (f.id, Int.MaxValue),
)
}
55 changes: 46 additions & 9 deletions src/main/scala/esmeta/analyzer/DomainLike.scala
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
package esmeta.analyzer

import esmeta.util.Appender.*
import esmeta.util.BaseUtils.*

trait DomainLikeDecl { self: Analyzer =>

/** abstract states */
trait AbsStateLike {
/** abstract domain */
trait DomainLike[Elem] {

/** has imprecise elements */
def hasImprec: Boolean
/** top element */
def Top: Elem

/** bottom element */
def Bot: Elem

/** appender */
given rule: Rule[Elem]
}

/** abstract return values */
trait AbsRetLike {
trait DomainElemLike[Elem] { self: Elem =>

/** return value */
def value: AbsValue
/** abstract domain */
def domain: DomainLike[Elem]

/** conversion to string */
override def toString: String = stringify(this)(using domain.rule)
}

/** abstract values */
trait AbsValueLike {
trait AbsValueLike extends DomainElemLike[AbsValue] { self: AbsValue =>

/** abstract domain */
def domain = AbsValue

/** get string of abstract value with an abstract state */
def getString(state: AbsState): String
}
val AbsValue: DomainLike[AbsValue]

/** abstract states */
trait AbsStateLike extends DomainElemLike[AbsState] { self: AbsState =>

/** abstract domain */
def domain = AbsState

/** has imprecise elements */
def hasImprec: Boolean
}
val AbsState: DomainLike[AbsState]

/** abstract return values */
trait AbsRetLike extends DomainElemLike[AbsRet] { self: AbsRet =>

/** abstract domain */
def domain = AbsRet

/** return value */
def value: AbsValue
}
val AbsRet: DomainLike[AbsRet]
}
Loading

0 comments on commit e279372

Please sign in to comment.