AccessException -< IllegalAccessException; added TODO() and NotImplementedException

This commit is contained in:
Sergey Chernov 2026-01-05 11:14:05 +01:00
parent e0a59c8db6
commit 514ad96148
9 changed files with 107 additions and 42 deletions

View File

@ -93,8 +93,6 @@ If we want to evaluate the message lazily:
In this case, formatting will only occur if the condition is not met.
### `check`
check(condition, message="check failed")
@ -107,3 +105,17 @@ With lazy message evaluation:
In this case, formatting will only occur if the condition is not met.
### TODO
It is easy to mark some code and make it throw a special exception at cone with:
TODO()
or
TODO("some message")
It raises an `NotImplementedException` with the given message. You can catch it
as any other exception when necessary.
Many IDE and editors have built-in support for marking code with TODOs.

View File

@ -21,6 +21,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.yield
import net.sergeych.lyng.Script.Companion.defaultImportManager
import net.sergeych.lyng.miniast.addConstDoc
import net.sergeych.lyng.miniast.addFnDoc
import net.sergeych.lyng.miniast.addVoidFnDoc
import net.sergeych.lyng.miniast.type
import net.sergeych.lyng.obj.*
@ -204,18 +205,32 @@ class Script(
)
)
}
addFn("assertThrows") {
addFnDoc(
"assertThrows",
doc = """
Asserts that the provided code block throws an exception, with or without exception:
```lyng
assertThrows { /* ode */ }
assertThrows(IllegalArgumentException) { /* code */ }
```
If an expected exception class is provided,
it checks that the thrown exception is of that class. If no expected class is provided, any exception
will be accepted.
""".trimIndent()
) {
val code: Statement
val expectedClass: ObjClass?
when(args.size) {
when (args.size) {
1 -> {
code = requiredArg<Statement>(0)
expectedClass = null
}
2 -> {
code = requiredArg<Statement>(1)
expectedClass = requiredArg<ObjClass>(0)
}
else -> raiseIllegalArgument("Expected 1 or 2 arguments, got ${args.size}")
}
val result = try {
@ -226,10 +241,15 @@ class Script(
} catch (_: ScriptError) {
ObjNull
}
if( result == null ) raiseError(ObjAssertionFailedException(this, "Expected exception but nothing was thrown"))
if (result == null) raiseError(
ObjAssertionFailedException(
this,
"Expected exception but nothing was thrown"
)
)
expectedClass?.let {
if( result !is ObjException)
raiseError("Expected $expectedClass, got $result")
if (result !is ObjException)
raiseError("Expected $expectedClass, got non-lyng exception $result")
if (result.exceptionClass != expectedClass) {
raiseError("Expected $expectedClass, got ${result.exceptionClass}")
}
@ -255,7 +275,7 @@ class Script(
val condition = requiredArg<ObjBool>(0)
if (!condition.value) {
var message = args.list.getOrNull(1)
if( message is Statement ) message = message.execute(this)
if (message is Statement) message = message.execute(this)
raiseIllegalArgument(message?.toString() ?: "requirement not met")
}
ObjVoid
@ -264,7 +284,7 @@ class Script(
val condition = requiredArg<ObjBool>(0)
if (!condition.value) {
var message = args.list.getOrNull(1)
if( message is Statement ) message = message.execute(this)
if (message is Statement) message = message.execute(this)
raiseIllegalState(message?.toString() ?: "check failed")
}
ObjVoid

View File

@ -93,7 +93,7 @@ open class Obj {
val decl = rec.declaringClass ?: cls
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
val saved = scope.currentClassCtx
scope.currentClassCtx = decl
try {
@ -117,7 +117,7 @@ open class Obj {
val decl = rec.declaringClass ?: cls
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke ${name}: not visible (declared in ${decl.className}, caller ${caller?.className ?: "?"})"))
val saved = scope.currentClassCtx
scope.currentClassCtx = decl
try {
@ -387,7 +387,7 @@ open class Obj {
val caller = scope.currentClassCtx
// Check visibility for non-property members here if they weren't checked before
if (!canAccessMember(obj.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field ${name}: not visible (declared in ${decl?.className ?: "?"}, caller ${caller?.className ?: "?"})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't access field ${name}: not visible (declared in ${decl?.className ?: "?"}, caller ${caller?.className ?: "?"})"))
return obj
}
@ -424,7 +424,7 @@ open class Obj {
val decl = field.declaringClass
val caller = scope.currentClassCtx
if (!canAccessMember(field.effectiveWriteVisibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't assign field ${name}: not visible (declared in ${decl?.className ?: "?"}, caller ${caller?.className ?: "?"})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't assign field ${name}: not visible (declared in ${decl?.className ?: "?"}, caller ${caller?.className ?: "?"})"))
if (field.value is ObjProperty) {
(field.value as ObjProperty).callSetter(scope, this, newValue, decl)
} else if (field.isMutable) field.value = newValue else scope.raiseError("can't assign to read-only field: $name")

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -191,11 +191,12 @@ open class ObjException(
"IllegalAssignmentException",
"SymbolNotDefinedException",
"IterationEndException",
"AccessException",
"IllegalAccessException",
"UnknownException",
"NotFoundException",
"IllegalOperationException",
"UnsetException",
"NotImplementedException",
"SyntaxError"
)) {
scope.addConst(name, getOrCreateExceptionClass(name))
@ -236,8 +237,8 @@ class ObjSymbolNotDefinedException(scope: Scope, message: String = "symbol is no
class ObjIterationFinishedException(scope: Scope) :
ObjException("IterationEndException", scope, "iteration finished")
class ObjAccessException(scope: Scope, message: String = "access not allowed error") :
ObjException("AccessException", scope, message)
class ObjIllegalAccessException(scope: Scope, message: String = "access not allowed error") :
ObjException("IllegalAccessException", scope, message)
class ObjUnknownException(scope: Scope, message: String = "access not allowed error") :
ObjException("UnknownException", scope, message)
@ -250,3 +251,6 @@ class ObjNotFoundException(scope: Scope, message: String = "not found") :
class ObjUnsetException(scope: Scope, message: String = "property is unset (not initialized)") :
ObjException("UnsetException", scope, message)
class ObjNotImplementedException(scope: Scope, message: String = "not implemented") :
ObjException("NotImplementedException", scope, message)

View File

@ -40,7 +40,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't access field $name (declared in ${decl?.className ?: "?"})"
)
@ -81,7 +81,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, declaring, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't access field $name (declared in ${declaring?.className ?: "?"})"
)
@ -104,7 +104,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
if (scope.thisObj !== this || scope.currentClassCtx == null) {
val caller = scope.currentClassCtx
if (!canAccessMember(f.effectiveWriteVisibility, decl, caller))
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't assign to field $name (declared in ${decl?.className ?: "?"})"
).raise()
@ -139,7 +139,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
if (scope.thisObj !== this || scope.currentClassCtx == null) {
val caller = scope.currentClassCtx
if (!canAccessMember(rec.effectiveWriteVisibility, declaring, caller))
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't assign to field $name (declared in ${declaring?.className ?: "?"})"
).raise()
@ -168,7 +168,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
)
@ -189,7 +189,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
val caller = scope.currentClassCtx ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
)
@ -319,7 +319,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = rec.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't access field $name (declared in ${decl.className})"))
return rec
}
// Then try instance locals (unmangled) only if startClass is the dynamic class itself
@ -329,7 +329,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't access field $name (declared in ${decl?.className ?: "?"})"
)
@ -342,7 +342,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = r.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!canAccessMember(r.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't access field $name (declared in ${decl.className})"))
return when (val value = r.value) {
is net.sergeych.lyng.Statement -> ObjRecord(
value.execute(
@ -364,7 +364,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = f.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!canAccessMember(f.effectiveWriteVisibility, decl, caller))
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't assign to field $name (declared in ${decl.className})"
).raise()
@ -378,7 +378,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = f.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx
if (!canAccessMember(f.effectiveWriteVisibility, decl, caller))
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't assign to field $name (declared in ${decl?.className ?: "?"})"
).raise()
@ -391,7 +391,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = r.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!canAccessMember(r.effectiveWriteVisibility, decl, caller))
ObjAccessException(scope, "can't assign to field $name (declared in ${decl.className})").raise()
ObjIllegalAccessException(scope, "can't assign to field $name (declared in ${decl.className})").raise()
if (!r.isMutable) scope.raiseError("can't assign to read-only field: $name")
if (r.value.assign(scope, newValue) == null) r.value = newValue
}
@ -407,7 +407,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val decl = rec.declaringClass ?: startClass
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
scope.raiseError(ObjIllegalAccessException(scope, "can't invoke method $name (declared in ${decl.className})"))
val saved = instance.instanceScope.currentClassCtx
instance.instanceScope.currentClassCtx = decl
try {
@ -423,7 +423,7 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
val caller = scope.currentClassCtx
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(
ObjAccessException(
ObjIllegalAccessException(
scope,
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -595,7 +595,7 @@ class FieldRef(
val scope0 = (obj as ObjClass).classScope!!
val r0 = scope0.getSlotRecord(capturedIdx)
if (!r0.visibility.isPublic)
sc.raiseError(ObjAccessException(sc, "can't access non-public field $name"))
sc.raiseError(ObjIllegalAccessException(sc, "can't access non-public field $name"))
r0
}
} else {
@ -631,7 +631,7 @@ class FieldRef(
// visibility/mutability checks
if (!rec.isMutable) scope.raiseError(ObjIllegalAssignmentException(scope, "can't reassign val $name"))
if (!rec.visibility.isPublic)
scope.raiseError(ObjAccessException(scope, "can't access non-public field $name"))
scope.raiseError(ObjIllegalAccessException(scope, "can't access non-public field $name"))
if (rec.value.assign(scope, newValue) == null) rec.value = newValue
return
}
@ -1195,7 +1195,7 @@ class MethodCallRef(
mKey1 = key; mVer1 = ver; mInvoker1 = { obj, sc, a ->
val inst = obj as ObjInstance
if (!visibility.isPublic && !canAccessMember(visibility, hierarchyMember.declaringClass ?: inst.objClass, sc.currentClassCtx))
sc.raiseError(ObjAccessException(sc, "can't invoke non-public method $name"))
sc.raiseError(ObjIllegalAccessException(sc, "can't invoke non-public method $name"))
callable.invoke(inst.instanceScope, inst, a)
}
} else {
@ -1467,7 +1467,7 @@ class BoundLocalVarRef(
scope.pos = atPos
val rec = scope.getSlotRecord(slot)
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx))
scope.raiseError(ObjAccessException(scope, "private field access"))
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
return rec
}
@ -1475,7 +1475,7 @@ class BoundLocalVarRef(
scope.pos = atPos
val rec = scope.getSlotRecord(slot)
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx))
scope.raiseError(ObjAccessException(scope, "private field access"))
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
return rec.value
}
@ -1483,7 +1483,7 @@ class BoundLocalVarRef(
scope.pos = atPos
val rec = scope.getSlotRecord(slot)
if (rec.declaringClass != null && !canAccessMember(rec.visibility, rec.declaringClass, scope.currentClassCtx))
scope.raiseError(ObjAccessException(scope, "private field access"))
scope.raiseError(ObjIllegalAccessException(scope, "private field access"))
if (!rec.isMutable) scope.raiseError("Cannot assign to immutable value")
rec.value = newValue
}

View File

@ -488,7 +488,7 @@ class OOTest {
fun setValue(newValue) { y = newValue }
}
assertEquals(100, A().y)
assertThrows(AccessException) { A().y = 200 }
assertThrows(IllegalAccessException) { A().y = 200 }
val a = A()
a.setValue(200)
assertEquals(200, a.y)
@ -502,7 +502,7 @@ class OOTest {
}
val c = C(10)
assertEquals(10, c.y)
assertThrows(AccessException) { c.y = 20 }
assertThrows(IllegalAccessException) { c.y = 20 }
c.setBValue(30)
assertEquals(30, c.y)
@ -515,7 +515,7 @@ class OOTest {
}
val d = D()
assertEquals(0, d.y)
assertThrows(AccessException) { d.y = 10 }
assertThrows(IllegalAccessException) { d.y = 10 }
d.setY(20)
assertEquals(20, d.y)
"""

View File

@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -4532,4 +4532,32 @@ class ScriptTest {
""".trimIndent())
}
// @Test
// fun testUserClassExceptions() = runTest {
// eval("""
// val x = try { throw IllegalAccessException("test1") } catch { it }
// assertEquals("test1", x.message)
// assert( x is IllegalAccessException)
// assertThrows(IllegalAccessException) { throw IllegalAccessException("test2") }
//
// class X : Exception("test3")
// val y = try { throw X() } catch { it }
// println(y)
// assertEquals("test3", y.message)
// assert( y is X)
//
// """.trimIndent())
// }
@Test
fun testTodo() = runTest {
eval("""
assertThrows(NotImplementedException) {
TODO()
}
val x = try { TODO("check me") } catch { it }
assertEquals("check me", x.message)
""".trimIndent())
}
}

View File

@ -276,3 +276,4 @@ fun Exception.printStackTrace() {
/* Compile this string into a regular expression. */
val String.re get() = Regex(this)
fun TODO(message=null) = throw NotImplementedException(message ?: "not implemented")