AccessException -< IllegalAccessException; added TODO() and NotImplementedException
This commit is contained in:
parent
e0a59c8db6
commit
514ad96148
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 ?: "?"})"
|
||||
)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
"""
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user