fixed errors

This commit is contained in:
Sergey Chernov 2025-11-17 14:08:32 +01:00
parent aeeec2d417
commit 6eabcc315f
6 changed files with 72 additions and 26 deletions

View File

@ -21,7 +21,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
group = "net.sergeych"
version = "0.10.3-SNAPSHOT"
version = "0.11.1-SNAPSHOT"
buildscript {
repositories {

View File

@ -30,10 +30,11 @@ class ClosureScope(val callScope: Scope, val closureScope: Scope) :
Scope(callScope, callScope.args, thisObj = closureScope.thisObj) {
init {
// Preserve the lexical class context from where the lambda was defined (closure),
// so that visibility checks (private/protected) inside lambdas executed within other
// methods (e.g., Mutex.withLock) still see the original declaring class context.
this.currentClassCtx = closureScope.currentClassCtx
// Preserve the lexical class context of the closure by default. This ensures that lambdas
// created inside a class method keep access to that class's private/protected members even
// when executed from within another object's method (e.g., Mutex.withLock), which may set
// its own currentClassCtx temporarily. If the closure has no class context, inherit caller's.
this.currentClassCtx = closureScope.currentClassCtx ?: callScope.currentClassCtx
}
override fun get(name: String): ObjRecord? {

View File

@ -224,8 +224,19 @@ class Compiler(
while (true) {
val opToken = cc.next()
val op = byLevel[level][opToken.type]
val op = byLevel[level][opToken.type]
if (op == null) {
// handle ternary conditional at the top precedence level only: a ? b : c
if (opToken.type == Token.Type.QUESTION && level == 0) {
val thenRef = parseExpressionLevel(level + 1)
?: throw ScriptError(opToken.pos, "Expecting expression after '?'")
val colon = cc.next()
if (colon.type != Token.Type.COLON) colon.raiseSyntax("missing ':'")
val elseRef = parseExpressionLevel(level + 1)
?: throw ScriptError(colon.pos, "Expecting expression after ':'")
lvalue = ConditionalRef(lvalue!!, thenRef, elseRef)
continue
}
cc.previous()
break
}

View File

@ -62,7 +62,10 @@ open class ObjBuffer(val byteArray: UByteArray) : Obj() {
val size by byteArray::size
override fun hashCode(): Int {
return byteArray.hashCode()
// On some platforms (notably JS), UByteArray.hashCode() is not content-based.
// For map/set keys we must ensure hash is consistent with equals(contentEquals).
// Convert to ByteArray and use contentHashCode() which is value-based and stable.
return byteArray.asByteArray().contentHashCode()
}
override suspend fun compareTo(scope: Scope, other: Obj): Int {

View File

@ -32,9 +32,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
// Direct (unmangled) lookup first
instanceScope[name]?.let {
val decl = it.declaringClass ?: objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx ?: instanceScope.currentClassCtx
val allowed = if (it.visibility == net.sergeych.lyng.Visibility.Private) (decl === objClass) else canAccessMember(it.visibility, decl, caller)
if (!allowed)
// When execution passes through suspension points (e.g., withLock),
// currentClassCtx could be lost. Fall back to the instance scope class ctx
// to preserve correct visibility semantics for private/protected members
// accessed from within the class methods.
// Allow unconditional access when accessing through `this` of the same instance
if (scope.thisObj === this) return it
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 // do not default to objClass for outsiders
if (!canAccessMember(it.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl?.className ?: "?"})"))
return it
}
@ -51,15 +57,15 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
return null
}
findMangled()?.let { rec ->
val declName = rec.importedFrom?.packageName // unused; use mangled key instead
// derive declaring class by mangled prefix: try self then parents
val declaring = when {
instanceScope.objects.containsKey("${cls.className}::$name") -> cls
else -> cls.mroParents.firstOrNull { instanceScope.objects.containsKey("${it.className}::$name") }
}
val caller = scope.currentClassCtx ?: instanceScope.currentClassCtx
val allowed = if (rec.visibility == net.sergeych.lyng.Visibility.Private) (declaring === objClass) else canAccessMember(rec.visibility, declaring, caller)
if (!allowed)
if (scope.thisObj === this) return rec
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 // do not default to objClass for outsiders
if (!canAccessMember(rec.visibility, declaring, caller))
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${declaring?.className ?: "?"})"))
return rec
}
@ -71,10 +77,14 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
// Direct (unmangled) first
instanceScope[name]?.let { f ->
val decl = f.declaringClass ?: objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx ?: instanceScope.currentClassCtx
val allowed = if (f.visibility == net.sergeych.lyng.Visibility.Private) (decl === objClass) else canAccessMember(f.visibility, decl, caller)
if (!allowed)
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl?.className ?: "?"})").raise()
if (scope.thisObj === this) {
// direct self-assignment allowed; enforce mutability below
} else {
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 // do not default to objClass for outsiders
if (!canAccessMember(f.visibility, decl, caller))
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl?.className ?: "?"})").raise()
}
if (!f.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
if (f.value.assign(scope, newValue) == null)
f.value = newValue
@ -95,10 +105,12 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
instanceScope.objects.containsKey("${cls.className}::$name") -> cls
else -> cls.mroParents.firstOrNull { instanceScope.objects.containsKey("${it.className}::$name") }
}
val caller = scope.currentClassCtx ?: instanceScope.currentClassCtx
val allowed = if (rec.visibility == net.sergeych.lyng.Visibility.Private) (declaring === objClass) else canAccessMember(rec.visibility, declaring, caller)
if (!allowed)
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${declaring?.className ?: "?"})").raise()
if (scope.thisObj !== this) {
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 // do not default to objClass for outsiders
if (!canAccessMember(rec.visibility, declaring, caller))
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${declaring?.className ?: "?"})").raise()
}
if (!rec.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
if (rec.value.assign(scope, newValue) == null)
rec.value = newValue
@ -111,9 +123,9 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
onNotFoundResult: (()->Obj?)?): Obj =
instanceScope[name]?.let { rec ->
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx ?: instanceScope.currentClassCtx
val allowed = if (rec.visibility == net.sergeych.lyng.Visibility.Private) (decl === objClass) else canAccessMember(rec.visibility, decl, caller)
if (!allowed)
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
// execute with lexical class context propagated to declaring class
val saved = instanceScope.currentClassCtx
@ -131,7 +143,8 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
// fallback: class-scope function (registered during class body execution)
objClass.classScope?.objects?.get(name)?.let { rec ->
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
val caller = scope.currentClassCtx
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
if (!canAccessMember(rec.visibility, decl, caller))
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
val saved = instanceScope.currentClassCtx

View File

@ -253,6 +253,24 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r
}
}
/** Conditional (ternary) operator reference: cond ? a : b */
class ConditionalRef(
private val condition: ObjRef,
private val ifTrue: ObjRef,
private val ifFalse: ObjRef
) : ObjRef {
override suspend fun get(scope: Scope): ObjRecord {
val condVal = if (net.sergeych.lyng.PerfFlags.RVAL_FASTPATH) condition.evalValue(scope) else condition.get(scope).value
val condTrue = when (condVal) {
is ObjBool -> condVal.value
is ObjInt -> condVal.value != 0L
else -> condVal.toBool()
}
val branch = if (condTrue) ifTrue else ifFalse
return branch.get(scope)
}
}
/** Cast operator reference: left `as` rightType or `as?` (nullable). */
class CastRef(
private val valueRef: ObjRef,