Skip to content

Commit

Permalink
Support ThrowCompletion and type guard for CreateListFromArrayLike
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnaldo committed Sep 14, 2024
1 parent 67dd3a6 commit d77fb22
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 20 deletions.
10 changes: 10 additions & 0 deletions src/main/resources/manuals/types
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,22 @@ type CompletionRecord {
// https://tc39.es/ecma262/#sec-completion-record-specification-type
type NormalCompletion extends CompletionRecord {
Type: Enum[~normal~];
Value;
Target: Enum[~empty~];
}

// https://tc39.es/ecma262/#sec-completion-record-specification-type
type AbruptCompletion extends CompletionRecord {
Type: Enum[~break~, ~continue~, ~return~, ~throw~];
Value;
Target: String | Enum[~empty~];
}

// https://tc39.es/ecma262/2024/#sec-throwcompletion
type ThrowCompletion extends AbruptCompletion {
Type: Enum[~throw~];
Value: ESValue;
Target: Enum[~empty~];
}

// -----------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/result/spec-summary
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
- yet: 362
- unknown: 625
- tables: 94
- type model: 87
- type model: 88
23 changes: 12 additions & 11 deletions src/main/scala/esmeta/analyzer/TypeAnalyzer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,17 @@ class TypeAnalyzer(
xs(0).map { x => map += Normal -> Map(x -> RecordT("Constructor")) }
AbsValue(retTy, map)
},
"CreateListFromArrayLike" -> { (xs, vs, retTy) =>
AbsValue((for {
v <- vs.lift(1)
str = v.ty.list.elem.str
ss <- str match
case Inf => None
case Fin(ss) => Some(ss)
ty = ss.map(ValueTy.fromTypeOf).foldLeft(BotT)(_ || _)
refined = retTy.toValue && NormalT(ListT(ty))
} yield refined).getOrElse(retTy))
},
"IsUnresolvableReference" -> { (xs, vs, retTy) =>
var map: Refinements = Map()
xs(0).map { x =>
Expand Down Expand Up @@ -655,17 +666,7 @@ class TypeAnalyzer(
rty = rv.ty
refinedV = rty.str.getSingle match
case One(tname) =>
val value = AbsValue(tname match
case "Object" => RecordT("Object")
case "Symbol" => SymbolT
case "Number" => NumberT
case "BigInt" => BigIntT
case "String" => StrT
case "Boolean" => BoolT
case "Undefined" => UndefT
case "Null" => NullT
case _ => ValueTy(),
)
val value = AbsValue(ValueTy.fromTypeOf(tname))
if (positive) lv ⊓ value else lv -- value
case _ => lv
_ <- modify(_.update(l, refinedV))
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/esmeta/ty/ListTy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ enum ListTy extends TyElem with Lattice[ListTy] {
def normalized: ListTy = this match
case Elem(elem) if elem.isTop => Top
case _ => this

/** mapping function */
def map(f: ValueTy => ValueTy): ListTy = this match
case Elem(elem) => Elem(f(elem))
case _ => this
}
object ListTy extends Parser.From(Parser.listTy) {
def apply(elem: ValueTy): ListTy = Elem(elem).normalized
Expand Down
12 changes: 12 additions & 0 deletions src/main/scala/esmeta/ty/ValueTy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,16 @@ object ValueTy extends Parser.From(Parser.valueTy) {
).norm
lazy val Top: ValueTy = ValueTopTy
lazy val Bot: ValueTy = ValueElemTy()

/** get type from `typeof` string */
def fromTypeOf(str: String): ValueTy = str match
case "Object" => RecordT("Object")
case "Symbol" => SymbolT
case "Number" => NumberT
case "BigInt" => BigIntT
case "String" => StrT
case "Boolean" => BoolT
case "Undefined" => UndefT
case "Null" => NullT
case _ => Bot
}
2 changes: 1 addition & 1 deletion src/main/scala/esmeta/ty/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ lazy val AbruptT: ValueTy = ValueTy(record = RecordTy("AbruptCompletion"))
def AbruptT(xs: String*): ValueTy = AbruptT(xs.toSet)
def AbruptT(xs: Set[String]): ValueTy =
ValueTy(record = RecordTy("AbruptCompletion", Map("Type" -> EnumT(xs.toSet))))
lazy val ThrowT: ValueTy = AbruptT("throw")
lazy val ThrowT: ValueTy = RecordT("ThrowCompletion")
lazy val NormalT: ValueTy = ValueTy(record = RecordTy("NormalCompletion"))
def NormalT(value: ValueTy): ValueTy =
if (value.isBottom) BotT
Expand Down
18 changes: 11 additions & 7 deletions src/main/scala/esmeta/ty/util/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,17 @@ trait Parsers extends BasicParsers {
"Bot" ^^^ BotT |
// completion record
"Completion" ^^^ CompT |
"Normal" ~> opt("[" ~> valueTy <~ "]") ^^ {
case None => NormalT
case Some(v) => NormalT(v)
} | "Abrupt" ~> opt("[" ~> rep1sep(ident, ",") <~ "]") ^^ {
case None => AbruptT
case Some(names) => AbruptT(names.toSet)
} |
"Normal" ~> opt(
"[" ~> valueTy <~ "]" ^^ { NormalT(_) } |
fieldMap ^^ { RecordT("NormalCompletion", _) },
) ^^ { _.getOrElse(NormalT) } |
"Abrupt" ~> opt(
"[" ~> rep1sep(ident, ",") <~ "]" ^^ { xs => AbruptT(xs.toSet) } |
fieldMap ^^ { RecordT("AbruptCompletion", _) },
) ^^ { _.getOrElse(AbruptT) } |
"Throw" ~> opt(
fieldMap ^^ { RecordT("ThrowCompletion", _) },
) ^^ { _.getOrElse(ThrowT) } |
// ECMAScript value
"ESValue" ^^^ ESValueT |
// closure
Expand Down
5 changes: 5 additions & 0 deletions src/main/scala/esmeta/ty/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ object Stringifier {
if (fm.map.keySet == Set("Type")) app >> fm("Type").value.enumv
else if (!fm.isTop) app >> " " >> fm
}
map.get("ThrowCompletion").map { fm =>
m -= "ThrowCompletion"
mayOR >> "Throw"
if (!fm.isTop) app >> " " >> fm
}
if (m.nonEmpty)
if (prevExists) app >> OR
app >> "Record[" >> m.toList.sortBy(_._1) >> "]"
Expand Down

0 comments on commit d77fb22

Please sign in to comment.