Skip to content

Commit

Permalink
Implement __NEW_ERROR_OBJ__ aux function to simplify `throw a SomeE…
Browse files Browse the repository at this point in the history
…rror exception`. (#247)

Co-authored-by: Jihyeok Park <jihyeok_park@korea.ac.kr>
  • Loading branch information
hyp3rflow and jhnaldo committed Jul 25, 2024
1 parent 98fd291 commit 0233041
Show file tree
Hide file tree
Showing 16 changed files with 78 additions and 34 deletions.
4 changes: 3 additions & 1 deletion src/main/resources/manuals/funcs/BigInt::exponentiate.ir
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ def <NUM>:BigInt::exponentiate(
exponent: BigInt,
): Normal[BigInt] | Abrupt[throw] = {
if (< exponent 0n) {
return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%RangeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %0 = clo<__NEW_ERROR_OBJ__>("%RangeError.prototype%")
call %1 = clo<ThrowCompletion>(%0)
return %1
}
if (&& (= base 0n) (= exponent 0n)) {
return 1n
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ def <BUILTIN>:INTRINSICS.Function.prototype.toString(
return "function () { [native code] }"
}

return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %1 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %2 = clo<ThrowCompletion>(%1)
return %2
}
8 changes: 6 additions & 2 deletions src/main/resources/manuals/funcs/RequireObjectCoercible.ir
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ def RequireObjectCoercible(
argument: ESValue,
): Normal[ESValue] | Abrupt[throw] = {
if (= (typeof argument) @Undefined) {
return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %0 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %1 = clo<ThrowCompletion>(%0)
return %1
}
if (= (typeof argument) @Null) {
return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %2 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %3 = clo<ThrowCompletion>(%2)
return %3
}
if (= (typeof argument) @Boolean) return argument
if (= (typeof argument) @Number) return argument
Expand Down
34 changes: 27 additions & 7 deletions src/main/resources/manuals/funcs/ToBigInt.ir
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,39 @@ def ToBigInt(
): Normal[BigInt] | Abrupt[throw] = {
call %0 = clo<ToPrimitive>(argument, ~number~)
let prim = [? %0]
if (= (typeof prim) @Undefined) return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
if (= (typeof prim) @Null) return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
if (= (typeof prim) @Undefined) {
call %1 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %2 = clo<ThrowCompletion>(%1)
return %2
}
if (= (typeof prim) @Null) {
call %3 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %4 = clo<ThrowCompletion>(%3)
return %4
}
if (= (typeof prim) @Boolean) {
if prim return 1n else return 0n
}
if (= (typeof prim) @BigInt) return prim
if (= (typeof prim) @Number) return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
if (= (typeof prim) @Number) {
call %5 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %6 = clo<ThrowCompletion>(%5)
return %6
}
if (= (typeof prim) @String) {
call %1 = clo<StringToBigInt>(prim)
let n = %1
if (= n undefined) return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%SyntaxError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %7 = clo<StringToBigInt>(prim)
let n = %7
if (= n undefined) {
call %8 = clo<__NEW_ERROR_OBJ__>("%SyntaxError.prototype%")
call %9 = clo<ThrowCompletion>(%8)
return %9
}
return n
}
if (= (typeof prim) @Symbol) return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
if (= (typeof prim) @Symbol) {
call %10 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %11 = clo<ThrowCompletion>(%10)
return %11
}
assert false
}
8 changes: 6 additions & 2 deletions src/main/resources/manuals/funcs/ToObject.ir
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ def ToObject(
argument: ESValue,
): Normal[Object] | Abrupt[throw] = {
if (= (typeof argument) @Undefined) {
return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %0 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %1 = clo<ThrowCompletion>(%0)
return %1
}
if (= (typeof argument) @Null) {
return comp[~throw~/~empty~]((new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%TypeError.prototype%"], "ErrorData" : undefined, "__MAP__" : (map) }))
call %2 = clo<__NEW_ERROR_OBJ__>("%TypeError.prototype%")
call %3 = clo<ThrowCompletion>(%2)
return %3
}
if (= (typeof argument) @Boolean) {
return (new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics["%Boolean.prototype%"], "BooleanData" : argument, "Extensible" : true, "__MAP__" : (map) })
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/manuals/funcs/__NEW_ERROR_OBJ__.ir
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def <AUX>:__NEW_ERROR_OBJ__(
proto: String,
): OrdinaryObject = {
return (new OrdinaryObject { "Prototype" : @EXECUTION_STACK[0].Realm.Intrinsics[proto], "ErrorData" : undefined, "__MAP__" : (map) } )
}
2 changes: 1 addition & 1 deletion src/main/resources/manuals/rule.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"If all Job Queues are empty, the result is implementation-defined.": "if (= @JOB_QUEUE.length 0) return errors",
"If no such execution context exists, return *null*. Otherwise, return _ec_'s ScriptOrModule.": "if (= ec absent) return null else return ec.ScriptOrModule",
"If only one argument was passed, return _to_.": "if (= ArgumentsList.length 0) return to",
"If the binding for _N_ in _envRec_ is an uninitialized binding, throw a *ReferenceError* exception.": "if (! envRec.__MAP__[N].initialized) return comp[~throw~/~empty~]((new OrdinaryObject { \"Prototype\" : @EXECUTION_STACK[0].Realm.Intrinsics[\"%ReferenceError.prototype%\"], \"ErrorData\" : undefined, \"__MAP__\" : (map) }))",
"If the binding for _N_ in _envRec_ is an uninitialized binding, throw a *ReferenceError* exception.": "if (! envRec.__MAP__[N].initialized) { call __errObj__ = clo<__NEW_ERROR_OBJ__>(\"%ReferenceError.prototype%\") call __comp__ = clo<ThrowCompletion>(__errObj__) return __comp__ }",
"If the execution context stack is empty, return *null*.": "if (= @EXECUTION_STACK.length 0) return null",
"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",
"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",
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/result/complete-funcs
Original file line number Diff line number Diff line change
Expand Up @@ -2518,4 +2518,5 @@ YieldExpression[2,0].Evaluation
__CLAMP__
__HAS_DUPLICATE__
__IS_ARRAY_INDEX__
__REMOVE_ELEM__
__NEW_ERROR_OBJ__
__REMOVE_ELEM__
25 changes: 13 additions & 12 deletions src/main/scala/esmeta/compiler/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,15 @@ class Compiler(
},
),
)
case ThrowStep(expr) =>
val comp = EComp(EENUM_THROW, compile(fb, expr), EENUM_EMPTY)
fb.addInst(IReturn(comp))
case ThrowStep(name) =>
val (x, xExpr) = fb.newTIdWithExpr
val (y, yExpr) = fb.newTIdWithExpr
val proto = EStr(Intrinsic(name, List("prototype")).toString)
fb.addInst(
ICall(x, AUX_NEW_ERROR_OBJ, List(proto)),
ICall(y, EClo("ThrowCompletion", Nil), List(xExpr)),
IReturn(yExpr),
)
case PerformStep(expr) =>
compile(fb, expr) match
case era: EReturnIfAbrupt => fb.addInst(IExpr(era))
Expand Down Expand Up @@ -756,15 +762,10 @@ class Compiler(
val (lhs, rhsIdx) = getProductionData(lhsName, rhsName)
ESyntactic(lhsName, lhs.params.map(_ => true), rhsIdx, Nil)
case ErrorObjectLiteral(name) =>
val proto = Intrinsic(name, List("prototype"))
ERecord(
"OrdinaryObject",
List(
"Prototype" -> toEIntrinsic(currentIntrinsics, proto),
"ErrorData" -> EUndef(),
INNER_MAP -> EMap(Nil),
),
)
val proto = EStr(Intrinsic(name, List("prototype")).toString)
val (x, xExpr) = fb.newTIdWithExpr
ICall(x, AUX_NEW_ERROR_OBJ, List(proto))
xExpr
case _: PositiveInfinityMathValueLiteral => EInfinity(pos = true)
case _: NegativeInfinityMathValueLiteral => EInfinity(pos = false)
case DecimalMathValueLiteral(n) => EMath(n)
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/esmeta/ir/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ def AUX_CLAMP = getAux("CLAMP")
def AUX_HAS_DUPLICATE = getAux("HAS_DUPLICATE")
def AUX_IS_ARRAY_INDEX = getAux("IS_ARRAY_INDEX")
def AUX_REMOVE_ELEM = getAux("REMOVE_ELEM")
def AUX_NEW_ERROR_OBJ = getAux("NEW_ERROR_OBJ")
2 changes: 1 addition & 1 deletion src/main/scala/esmeta/lang/Step.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ case class ForEachParseNodeStep(
) extends Step

// throw steps
case class ThrowStep(expr: Expression) extends Step
case class ThrowStep(name: String) extends Step

// perform steps
case class PerformStep(expr: Expression) extends Step
Expand Down
8 changes: 6 additions & 2 deletions src/main/scala/esmeta/lang/util/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ trait Parsers extends IndentParsers {

// throw steps
lazy val throwStep: PL[ThrowStep] =
"throw" ~> errObjLiteral <~ end ^^ { ThrowStep(_) }
lazy val errorName =
"*" ~> word.filter(_.endsWith("Error")) <~ "*"
"throw" ~> ("a" | "an") ~> errorName <~ "exception" <~ end ^^ {
ThrowStep(_)
}

// perform steps
lazy val performStep: PL[PerformStep] =
Expand Down Expand Up @@ -668,7 +672,7 @@ trait Parsers extends IndentParsers {
lazy val errorName = "*" ~> word.filter(_.endsWith("Error")) <~ "*" ^^ {
ErrorObjectLiteral(_)
}
"a newly created" ~> errorName <~ "object" | "a" ~> errorName <~ "exception"
"a newly created" ~> errorName <~ "object"

// clamping expression
lazy val clampExpr: PL[ClampExpression] =
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/esmeta/lang/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class Stringifier(detail: Boolean, location: Boolean) {
app >> First("for each child node ") >> x
app >> " of " >> expr >> ", do" >> body
case ThrowStep(expr) =>
app >> First("throw ") >> expr >> "."
app >> First("throw a *") >> expr >> "* exception."
case PerformStep(expr) =>
app >> First("perform ") >> expr >> "."
case PerformBlockStep(block) =>
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/esmeta/lang/JsonTinyTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class JsonTinyTest extends LangTest {
"for each own property key _x_ of _x_ such that _x_ and _x_, in descending chronological order of property creation, " +
"let _x_ be _x_."
),
throwStep -> "throw a newly created *TypeError* object.",
throwStep -> "throw a *ReferenceError* exception.",
performStep -> "perform ToObject(_x_ + _x_, -_x_).",
appendStep -> "append _x_ to _x_.[[Value]].",
prependStep -> "prepend _x_ to _x_.[[Value]].",
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/esmeta/lang/LangTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ object LangTest {
ForEachOwnPropertyKeyStepOrder.ChronologicalOrder,
letStep,
)
lazy val throwStep = ThrowStep(errObj)
lazy val throwStep = ThrowStep("ReferenceError")
lazy val performStep = PerformStep(invokeAOExpr)
lazy val appendStep = AppendStep(refExpr, fieldRef)
lazy val prependStep = PrependStep(refExpr, fieldRef)
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/esmeta/lang/StringifyTinyTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class StringifyTinyTest extends LangTest {
"for each own property key _x_ of _x_ such that _x_ and _x_, in descending chronological order of property creation, " +
"let _x_ be _x_."
),
throwStep -> "throw a newly created *TypeError* object.",
throwStep -> "throw a *ReferenceError* exception.",
performStep -> "perform ToObject(_x_ + _x_, -_x_).",
appendStep -> "append _x_ to _x_.[[Value]].",
prependStep -> "prepend _x_ to _x_.[[Value]].",
Expand Down

0 comments on commit 0233041

Please sign in to comment.