Skip to content

Commit

Permalink
Model initial state (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
h2oche committed Feb 22, 2022
1 parent 7c3a31d commit e87824f
Show file tree
Hide file tree
Showing 21 changed files with 789 additions and 27 deletions.
3 changes: 3 additions & 0 deletions src/main/resources/manuals/funcs/Function.prototype.ir
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def <BUILTIN>:Function.prototype(this: Any, argumentsList: Any, NewTarget: Any) {
return undefined
}
22 changes: 21 additions & 1 deletion src/main/resources/manuals/rule.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
{
"[YET] Let _realmRec_ be a new Realm Record.": "let realmRec = (new RealmRecord())[#0]",
"[YET] Let _intrinsics_ be a new Record.": "let intrinsics = (new Record())[#0]"
"[YET] Let _intrinsics_ be a new Record.": "let intrinsics = @INTRINSICS",
"[YET] Set fields of _intrinsics_ with the values listed in <emu-xref href=\"#table-well-known-intrinsic-objects\"></emu-xref>. The field names are the names listed in column one of the table. The value of each field is a new object value fully and recursively populated with property values as defined by the specification of each object in clauses <emu-xref href=\"#sec-global-object\"></emu-xref> through <emu-xref href=\"#sec-reflection\"></emu-xref>. All object property values are newly created object values. All values that are built-in function objects are created by performing CreateBuiltinFunction(_steps_, _length_, _name_, _slots_, _realmRec_, _prototype_) where _steps_ is the definition of that function provided by this specification, _name_ is the initial value of the function's `name` property, _length_ is the initial value of the function's `length` property, _slots_ is a list of the names, if any, of the function's specified internal slots, and _prototype_ is the specified value of the function's [[Prototype]] internal slot. The creation of the intrinsics and their properties must be ordered to avoid any dependencies upon objects that have not yet been created.": "intrinsics = @INTRINSICS",
"[YET] Assert: _realm_.[[Intrinsics]].[[%ThrowTypeError%]] exists and has been initialized.": "assert (! (= realm.Intrinsics.ThrowTypeError absent))",
"[YET] If _O_ does not have an own property with key _P_, return *undefined*.": "if (= O.SubMap[P] absent) then return undefined else {}",
"[YET] Create an own accessor property named _P_ of object _O_ whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attribute values are described by _Desc_. If the value of an attribute field of _Desc_ is absent, the attribute of the newly created property is set to its <emu-xref href=\"#table-object-property-attributes\">default value</emu-xref>.": "{ let ap = (new AccessorProperty())[#0] if (= Desc.Get absent) then ap.Get = undefined else ap.Get = Desc.Get if (= Desc.Set absent) then ap.Set = undefined else ap.Set = Desc.Set if (= Desc.Enumerable absent) then ap.Enumerable = false else ap.Enumerable = Desc.Enumerable if (= Desc.Configurable absent) then ap.Configurable = false else ap.Configurable = Desc.Configurable O.SubMap[P] = ap }",
"[YET] Let _newContext_ be a new execution context.": "let newContext = (new ExecutionContext())[#0]",
"[YET] If the host requires use of an exotic object to serve as _realm_'s global object, let _global_ be such an object created in a host-defined manner. Otherwise, let _global_ be *undefined*, indicating that an ordinary object should be created as the global object.": "let global = undefined",
"[YET] If the host requires that the `this` binding in _realm_'s global scope return an object other than the global object, let _thisValue_ be such an object created in a host-defined manner. Otherwise, let _thisValue_ be *undefined*, indicating that _realm_'s global `this` binding should be the global object.": "let thisValue = undefined",
"[YET] If _additionalInternalSlotsList_ is present, append each of its elements to _internalSlotsList_.": "if (! (= additionalInternalSlotsList absent)) then internalSlotsList = (list-concat internalSlotsList additionalInternalSlotsList)[#0] else {}",
"[YET] Let _obj_ be a newly created object with an internal slot for each name in _internalSlotsList_.": "{ let obj = (new OrdinaryObject())[#0] let idx = 0 loop[repeat] (< idx internalSlotsList.length) then { obj[internalSlotsList[idx]] = undefined idx = (+ idx 1) } }",
"[YET] Set _obj_'s essential internal methods to the default ordinary object definitions specified in <emu-xref href=\"#sec-ordinary-object-internal-methods-and-internal-slots\"></emu-xref>.": "nop",
"[YET] Assert: If the caller will not be overriding both _obj_'s [[GetPrototypeOf]] and [[SetPrototypeOf]] essential internal methods, then _internalSlotsList_ contains [[Prototype]].": "assert true",
"[YET] Assert: If the caller will not be overriding all of _obj_'s [[SetPrototypeOf]], [[IsExtensible]], and [[PreventExtensions]] essential internal methods, then _internalSlotsList_ contains [[Extensible]].": "assert true",
"[YET] Let _env_ be a new object Environment Record.": "let env = (new ObjectEnvironmentRecord())[#0]",
"[YET] Let _dclRec_ be a new declarative Environment Record containing no bindings.": "let dclRec = (new DeclarartiveEnvironmentRecord())[#0]",
"[YET] Let _env_ be a new global Environment Record.": "let env = (new GlobalEnvironmentRecord())[#0]",
"[YET] For each property of the Global Object specified in clause <emu-xref href=\"#sec-global-object\"></emu-xref>, do\n 1. [YET] Let _name_ be the String value of the property name.\n 1. [YET] Let _desc_ be the fully populated data Property Descriptor for the property, containing the specified attributes for the property. For properties listed in <emu-xref href=\"#sec-function-properties-of-the-global-object\"></emu-xref>, <emu-xref href=\"#sec-constructor-properties-of-the-global-object\"></emu-xref>, or <emu-xref href=\"#sec-other-properties-of-the-global-object\"></emu-xref> the value of the [[Value]] attribute is the corresponding intrinsic object from _realmRec_.\n 1. Perform ? DefinePropertyOrThrow(_global_, _name_, _desc_).": "{ let keys = (keys @GLOBAL_OBJECT.SubMap)[#0] let idx = 0 loop[repeat] (< idx keys.length) then { let name = keys[idx] global.SubMap[name] = @GLOBAL_OBJECT.SubMap[name] idx = (+ idx 1) } }",
"[YET] Create any host-defined global object properties on _globalObj_.": "nop",
"[YET] If the execution context stack is empty, return *null*.": "if (= @EXECUTION_STACK.length 0) then return null else {}",
"[YET] Let _ec_ be the topmost execution context on the execution context stack whose ScriptOrModule component is not *null*.": "{ let ec = absent let idx = @EXECUTION_STACK.length loop[repeat] (&& (< 0 idx) (= ec absent)) then { idx = (- idx 1) if (! (= @EXECUTION_STACK[idx].ScriptOrModule null) ) then ec = @EXECUTION_STACK[idx] else {}} }",
"[YET] If no such execution context exists, return *null*. Otherwise, return _ec_'s ScriptOrModule.": "if (= ec absent) then return null else return ec.ScriptOrModule"
}
17 changes: 16 additions & 1 deletion src/main/scala/esmeta/interp/Interp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import esmeta.error.*
import esmeta.interp.util.*
import esmeta.ir.{Func => IRFunc, *}
import esmeta.js.*
import esmeta.js.builtin.TypeModel
import esmeta.util.BaseUtils.*
import esmeta.util.SystemUtils.*
import esmeta.{TIMEOUT, TEST_MODE, DEBUG}
Expand All @@ -16,6 +17,7 @@ import scala.math.{BigInt => SBigInt}
class Interp(
val st: State,
val timeLimit: Option[Long] = Some(TIMEOUT),
val typeModel: Option[TypeModel] = None, // TODO refactoring
) {
import Interp.*

Expand All @@ -25,6 +27,10 @@ class Interp(
/** control flow graphs */
def cfg: CFG = st.cfg

/** type model */
// TODO refactoring
private given Option[TypeModel] = typeModel

/** step */
def step: Boolean =
try {
Expand Down Expand Up @@ -198,7 +204,16 @@ class Interp(
// TODO other cases
case (v, cop) => throw InvalidConversion(cop, expr, v)
}
case ETypeOf(base) => ??? // TODO discuss about the type
case ETypeOf(base) =>
// TODO discuss about the type
Str(interp(base).escaped match
case str: Str => "String"
case addr: Addr =>
st(addr) match
case map: MapObj => "Object"
case _ => ???
case _ => ???,
)
case ETypeCheck(expr, ty) => ??? // TODO discuss about the type
case EClo(fname, captured) =>
val func = cfg.fnameMap.getOrElse(fname, error("invalid function name"))
Expand Down
25 changes: 22 additions & 3 deletions src/main/scala/esmeta/interp/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import scala.collection.mutable.{Map => MMap}
import esmeta.cfg.*
import esmeta.ir.{Func => IRFunc, *}
import esmeta.js.*
import esmeta.js.builtin.TypeModel
import esmeta.util.BaseUtils.*
import esmeta.util.DoubleEquals

Expand Down Expand Up @@ -55,9 +56,9 @@ case class Heap(
// -----------------------------------------------------------------------------
sealed trait Obj extends InterpElem
case class MapObj(
var tname: String,
val props: MMap[PureValue, MapObj.Prop] = MMap(),
var size: Int = 0,
var ty: String, // TODO handle type
val props: MMap[PureValue, MapObj.Prop],
var size: Int,
) extends Obj
case class ListObj(var values: Vector[PureValue] = Vector()) extends Obj
case class SymbolObj(desc: PureValue) extends Obj
Expand All @@ -67,6 +68,24 @@ object MapObj:
/** property values */
case class Prop(value: Value, creationTime: Int)

/** apply with type model */
def apply(tname: String)(props: (PureValue, Value)*)(using
typeModel: Option[TypeModel],
): MapObj =
val obj = MapObj(tname)
for { ((k, v), idx) <- props.zipWithIndex }
obj.props += k -> Prop(v, idx + obj.size)
obj.size += props.size
obj

def apply(tname: String)(using typeModel: Option[TypeModel]): MapObj =
// get methods from type model
val methods = typeModel.fold(Map())(_.getMethods(tname))
val obj = MapObj(tname, MMap(), methods.size)
for { ((name, clo), idx) <- methods.zipWithIndex }
obj.props += Str(name) -> Prop(clo, idx)
obj

// -----------------------------------------------------------------------------
// Reference Value
// -----------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/esmeta/interp/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Stringifier(detail: Boolean, location: Boolean) {
app :> "context: " >> st.context
given Rule[List[String]] = iterableRule("[", ", ", "]")
app :> "call-stack: "
app.wrapIterable("[", "]")(st.globals)
app.wrapIterable("[", "]")(st.callStack)
app :> "globals: "
app.wrapIterable(st.globals)
app :> "heap: " >> st.heap
Expand Down
13 changes: 7 additions & 6 deletions src/main/scala/esmeta/interp/util/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package esmeta.interp.util

import esmeta.interp.*
import esmeta.ir.{Func => IRFunc, *}
import esmeta.js.builtin.TypeModel
import esmeta.cfg.*
import esmeta.cfg.util.*
import esmeta.error.*
Expand Down Expand Up @@ -204,7 +205,9 @@ extension (st: State) {
st.heap.copyObj(addr)
def keys(addr: Addr, intSorted: Boolean): Addr =
st.heap.keys(addr, intSorted)
def allocMap(tname: String, map: Map[PureValue, PureValue] = Map()): Addr =
def allocMap(tname: String, map: Map[PureValue, PureValue] = Map())(using
typeModel: Option[TypeModel],
): Addr =
st.heap.allocMap(tname, map)
def allocList(list: List[PureValue]): Addr =
st.heap.allocList(list)
Expand Down Expand Up @@ -253,8 +256,6 @@ extension (heap: Heap) {
heap.map.getOrElse(addr, throw UnknownAddr(addr))
def apply(addr: Addr, key: PureValue): Value = heap(addr) match
case (s: SymbolObj) => s(key)
// TODO case (MapObj(ALGORITHM, _, _)) => getAlgorithm(key)
// TODO case (MapObj(INTRINSICS, _, _)) => getIntrinsics(key)
case (m: MapObj) => m(key)
case (l: ListObj) => l(key)
case YetObj(_, msg) => throw NotSupported(msg)
Expand Down Expand Up @@ -307,7 +308,7 @@ extension (heap: Heap) {
def allocMap(
tname: String,
m: Map[PureValue, PureValue],
): Addr = {
)(using typeModel: Option[TypeModel]): Addr = {
val irMap =
if (tname == "Record") MapObj(tname, MMap(), 0) else MapObj(tname)
for ((k, v) <- m) irMap.update(k, v)
Expand Down Expand Up @@ -358,7 +359,7 @@ extension (heap: Heap) {
/** set type of objects */
def setType(addr: Addr, tname: String): heap.type = heap(addr) match {
case (irMap: MapObj) =>
irMap.tname = tname; heap
irMap.ty = tname; heap
case _ => error(s"invalid type update: $addr")
}

Expand Down Expand Up @@ -440,7 +441,7 @@ extension (map: MapObj) {
/** keys of map */
def keys(intSorted: Boolean): Vector[PureValue] = {
if (!intSorted) {
if (map.tname == "SubMap")
if (map.ty == "SubMap")
map.props.toVector
.sortBy(_._2._2)
.map(_._1)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/esmeta/ir/util/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ trait Parsers extends BasicParsers {

// functions
given func: Parser[Func] =
(main <~ "def") ~ funcKind ~ "(\\w|:)+".r ~ params ~ inst ^^ {
(main <~ "def") ~ funcKind ~ "[\\w|:\\.]+".r ~ params ~ inst ^^ {
case m ~ k ~ n ~ ps ~ b => Func(m, k, n, ps, b)
}

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

import esmeta.ir.*
import esmeta.interp.*
import esmeta.spec.*
import esmeta.js.builtin.*
import scala.collection.mutable.{Map => MMap}

object Initialize {
def apply(spec: Spec, sourceText: String): (State, TypeModel) = {
val cfg = spec.program.cfg
val typeModel = TypeModel(spec)
val st = State(
cfg,
Context(cfg.main),
globals = initGlobal(sourceText),
heap = initHeap(spec),
)
(st, typeModel)
}

// initial globals
private def initGlobal(sourceText: String): MMap[Global, Value] = MMap(
CONTEXT -> Null,
SOURCE_TEXT -> Str(sourceText),
EXECUTION_STACK -> NamedAddr(EXECUTION_STACK),
HOST_DEFINED -> Undef,
INTRINSICS -> NamedAddr(INTRINSICS),
GLOBAL_OBJECT -> NamedAddr(GLOBAL_OBJECT),
JOB_QUEUE -> NamedAddr(JOB_QUEUE),
SYMBOL_REGISTRY -> NamedAddr(SYMBOL_REGISTRY),
UNDEF_TYPE -> Str("Undefined"),
NULL_TYPE -> Str("Null"),
BOOL_TYPE -> Str("Boolean"),
STRING_TYPE -> Str("String"),
SYMBOL_TYPE -> Str("Symbol"),
NUMBER_TYPE -> Str("Number"),
BIGINT_TYPE -> Str("BigInt"),
OBJECT_TYPE -> Str("Object"),
).map { case (k, v) => Global(k) -> v }

// initial heaps
private def initHeap(spec: Spec): Heap = {
val intr = Intrinsics(spec)
val glob = GlobalObject(spec)

val map: MMap[Addr, Obj] = MMap(
NamedAddr(INTRINSICS) -> intr.obj,
NamedAddr(GLOBAL_OBJECT) -> glob.obj,
NamedAddr(EXECUTION_STACK) -> ListObj(),
NamedAddr(JOB_QUEUE) -> ListObj(),
NamedAddr(SYMBOL_REGISTRY) -> ListObj(),
)

// add intrinsics
// TODO add more intrinsics modeling
map ++= intr.map

// add global object
map ++= glob.map

Heap(map, map.size)
}

}
40 changes: 40 additions & 0 deletions src/main/scala/esmeta/js/builtin/GlobalObject.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package esmeta.js.builtin

import esmeta.interp.*
import esmeta.spec.*

/** model for global object */
case class GlobalObject(spec: Spec) {

/** shortcuts */
private val T = true
private val F = false
private val U = Undef
private def cfg = spec.program.cfg

/** type model */
// TODO refactoring
private given Option[TypeModel] = Some(TypeModel(spec))

/** get global object */
def obj: MapObj = MapObj("Object")(Str(SUBMAP) -> submapAddr(GLOBAL_OBJECT))

/** get map for heap */
lazy val map: Map[Addr, Obj] = {
var nmap = List(
"print" -> DataProperty(intrAddr("print"), T, F, T),
// TODO "globalThis" -> DataProperty(NamedAddr("GLOBAL"), T, F, T),
"Infinity" -> DataProperty(Number(Double.PositiveInfinity), F, F, F),
"NaN" -> DataProperty(Number(Double.NaN), F, F, F),
"undefined" -> DataProperty(Undef, F, F, F),
)
for {
row <- spec.tables(WELL_KNOWN_INTRINSICS).rows
List(intrCell, globCell) = row.take(2).map(_.trim) if globCell != ""
intrKey = intrCell.replace("%", "")
globKey = globCell.replace("`", "")
} { nmap ::= globKey -> DataProperty(intrAddr(intrKey), T, F, T) }

getSubmapObjects(GLOBAL_OBJECT, nmap)
}
}
Loading

0 comments on commit e87824f

Please sign in to comment.