fix $73 reg #74 val assignment bug fix. Also, cosmetics on syntax highlighting
This commit is contained in:
parent
e765784170
commit
603023962e
@ -2644,7 +2644,8 @@ class Compiler(
|
|||||||
// Defer: at instance construction, evaluate initializer in instance scope and store under mangled name
|
// Defer: at instance construction, evaluate initializer in instance scope and store under mangled name
|
||||||
val initStmt = statement(nameToken.pos) { scp ->
|
val initStmt = statement(nameToken.pos) { scp ->
|
||||||
val initValue = initialExpression?.execute(scp)?.byValueCopy() ?: ObjNull
|
val initValue = initialExpression?.execute(scp)?.byValueCopy() ?: ObjNull
|
||||||
scp.addOrUpdateItem(storageName, initValue, visibility, recordType = ObjRecord.Type.Field)
|
// Preserve mutability of declaration: do NOT use addOrUpdateItem here, as it creates mutable records
|
||||||
|
scp.addItem(storageName, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
|
||||||
ObjVoid
|
ObjVoid
|
||||||
}
|
}
|
||||||
cls.instanceInitializers += initStmt
|
cls.instanceInitializers += initStmt
|
||||||
@ -2652,7 +2653,8 @@ class Compiler(
|
|||||||
} else {
|
} else {
|
||||||
// We are in instance scope already: perform initialization immediately
|
// We are in instance scope already: perform initialization immediately
|
||||||
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
val initValue = initialExpression?.execute(context)?.byValueCopy() ?: ObjNull
|
||||||
context.addOrUpdateItem(storageName, initValue, visibility, recordType = ObjRecord.Type.Field)
|
// Preserve mutability of declaration: create record with correct mutability
|
||||||
|
context.addItem(storageName, isMutable, initValue, visibility, recordType = ObjRecord.Type.Field)
|
||||||
initValue
|
initValue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -172,6 +172,13 @@ class Script(
|
|||||||
if( a.compareTo(this, b) != 0 )
|
if( a.compareTo(this, b) != 0 )
|
||||||
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
|
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
|
||||||
}
|
}
|
||||||
|
// alias used in tests
|
||||||
|
addVoidFn("assertEqual") {
|
||||||
|
val a = requiredArg<Obj>(0)
|
||||||
|
val b = requiredArg<Obj>(1)
|
||||||
|
if( a.compareTo(this, b) != 0 )
|
||||||
|
raiseError(ObjAssertionFailedException(this,"Assertion failed: ${a.inspect(this)} == ${b.inspect(this)}"))
|
||||||
|
}
|
||||||
addVoidFn("assertNotEquals") {
|
addVoidFn("assertNotEquals") {
|
||||||
val a = requiredArg<Obj>(0)
|
val a = requiredArg<Obj>(0)
|
||||||
val b = requiredArg<Obj>(1)
|
val b = requiredArg<Obj>(1)
|
||||||
|
|||||||
@ -40,6 +40,8 @@ enum class HighlightKind {
|
|||||||
Label,
|
Label,
|
||||||
Directive,
|
Directive,
|
||||||
Error,
|
Error,
|
||||||
|
/** Enum constant (both declaration and usage). */
|
||||||
|
EnumConstant,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A highlighted span: character range and its semantic/lexical kind. */
|
/** A highlighted span: character range and its semantic/lexical kind. */
|
||||||
|
|||||||
@ -159,8 +159,10 @@ class SimpleLyngHighlighter : LyngHighlighter {
|
|||||||
}
|
}
|
||||||
if (range.endExclusive > range.start) raw += HighlightSpan(range, k)
|
if (range.endExclusive > range.start) raw += HighlightSpan(range, k)
|
||||||
}
|
}
|
||||||
|
// Heuristics: mark enum constants in declaration blocks and on qualified usages Foo.BAR
|
||||||
|
val overridden = applyEnumConstantHeuristics(text, src, tokens, raw)
|
||||||
// Adjust single-line comment spans to extend till EOL to compensate for lexer offset/length quirks
|
// Adjust single-line comment spans to extend till EOL to compensate for lexer offset/length quirks
|
||||||
val adjusted = extendSingleLineCommentsToEol(text, raw)
|
val adjusted = extendSingleLineCommentsToEol(text, overridden)
|
||||||
// Spans are in order; merge adjacent of the same kind for compactness
|
// Spans are in order; merge adjacent of the same kind for compactness
|
||||||
return mergeAdjacent(adjusted)
|
return mergeAdjacent(adjusted)
|
||||||
}
|
}
|
||||||
@ -202,3 +204,81 @@ private fun extendSingleLineCommentsToEol(
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect enum constants both in enum declarations and in qualified usages (TypeName.CONST)
|
||||||
|
* and override corresponding identifier spans with EnumConstant kind.
|
||||||
|
*/
|
||||||
|
private fun applyEnumConstantHeuristics(
|
||||||
|
text: String,
|
||||||
|
src: Source,
|
||||||
|
tokens: List<net.sergeych.lyng.Token>,
|
||||||
|
spans: MutableList<HighlightSpan>
|
||||||
|
): MutableList<HighlightSpan> {
|
||||||
|
if (tokens.isEmpty() || spans.isEmpty()) return spans
|
||||||
|
|
||||||
|
// Build quick lookup from range start to span index for identifiers only
|
||||||
|
val byStart = HashMap<Int, Int>(spans.size * 2)
|
||||||
|
for (i in spans.indices) {
|
||||||
|
val s = spans[i]
|
||||||
|
if (s.kind == HighlightKind.Identifier) byStart[s.range.start] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
fun overrideIdAtToken(idx: Int) {
|
||||||
|
val t = tokens[idx]
|
||||||
|
if (t.type != Type.ID) return
|
||||||
|
val start = src.offsetOf(t.pos)
|
||||||
|
val spanIndex = byStart[start] ?: return
|
||||||
|
spans[spanIndex] = HighlightSpan(spans[spanIndex].range, HighlightKind.EnumConstant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Enum declarations: enum Name { CONST1, CONST2 }
|
||||||
|
var i = 0
|
||||||
|
while (i < tokens.size) {
|
||||||
|
val t = tokens[i]
|
||||||
|
if (t.type == Type.ID && t.value.equals("enum", ignoreCase = true)) {
|
||||||
|
// expect: ID(enum) ID(name) LBRACE (ID (COMMA ID)* ) RBRACE
|
||||||
|
var j = i + 1
|
||||||
|
// skip optional whitespace/newlines tokens are separate types, so we just check IDs and braces
|
||||||
|
if (j < tokens.size && tokens[j].type == Type.ID) j++ else { i++; continue }
|
||||||
|
if (j < tokens.size && tokens[j].type == Type.LBRACE) {
|
||||||
|
j++
|
||||||
|
while (j < tokens.size) {
|
||||||
|
val tk = tokens[j]
|
||||||
|
if (tk.type == Type.RBRACE) { j++; break }
|
||||||
|
if (tk.type == Type.ID) {
|
||||||
|
// enum entry declaration
|
||||||
|
overrideIdAtToken(j)
|
||||||
|
j++
|
||||||
|
// optional comma
|
||||||
|
if (j < tokens.size && tokens[j].type == Type.COMMA) { j++ ; continue }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Any unexpected token ends enum entries scan
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = j
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Qualified usages: Something.CONST where CONST is ALL_UPPERCASE (with digits/underscores)
|
||||||
|
fun isAllUpperCase(name: String): Boolean = name.isNotEmpty() && name.all { it == '_' || it.isDigit() || (it.isLetter() && it.isUpperCase()) }
|
||||||
|
i = 1
|
||||||
|
while (i + 0 < tokens.size) {
|
||||||
|
val dotTok = tokens[i]
|
||||||
|
if (dotTok.type == Type.DOT && i + 1 < tokens.size) {
|
||||||
|
val next = tokens[i + 1]
|
||||||
|
if (next.type == Type.ID && isAllUpperCase(next.value)) {
|
||||||
|
overrideIdAtToken(i + 1)
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return spans
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package net.sergeych.lyng.obj
|
package net.sergeych.lyng.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
import net.sergeych.lyng.Arguments
|
import net.sergeych.lyng.Arguments
|
||||||
import net.sergeych.lyng.Scope
|
import net.sergeych.lyng.Scope
|
||||||
import net.sergeych.lyng.canAccessMember
|
import net.sergeych.lyng.canAccessMember
|
||||||
@ -41,11 +43,17 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 // do not default to objClass for outsiders
|
val caller = caller0 // do not default to objClass for outsiders
|
||||||
if (!canAccessMember(it.visibility, decl, caller))
|
if (!canAccessMember(it.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't access field $name (declared in ${decl?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
// Try MI-mangled lookup along linearization (C3 MRO): ClassName::name
|
// Try MI-mangled lookup along linearization (C3 MRO): ClassName::name
|
||||||
val cls = objClass
|
val cls = objClass
|
||||||
|
|
||||||
// self first, then parents
|
// self first, then parents
|
||||||
fun findMangled(): ObjRecord? {
|
fun findMangled(): ObjRecord? {
|
||||||
// self
|
// self
|
||||||
@ -66,7 +74,12 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 // do not default to objClass for outsiders
|
val caller = caller0 // do not default to objClass for outsiders
|
||||||
if (!canAccessMember(rec.visibility, declaring, caller))
|
if (!canAccessMember(rec.visibility, declaring, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${declaring?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't access field $name (declared in ${declaring?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
return rec
|
return rec
|
||||||
}
|
}
|
||||||
// Fall back to methods/properties on class
|
// Fall back to methods/properties on class
|
||||||
@ -83,7 +96,10 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 // do not default to objClass for outsiders
|
val caller = caller0 // do not default to objClass for outsiders
|
||||||
if (!canAccessMember(f.visibility, decl, caller))
|
if (!canAccessMember(f.visibility, decl, caller))
|
||||||
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl?.className ?: "?"})").raise()
|
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.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
if (f.value.assign(scope, newValue) == null)
|
if (f.value.assign(scope, newValue) == null)
|
||||||
@ -99,6 +115,7 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val rec = findMangled()
|
val rec = findMangled()
|
||||||
if (rec != null) {
|
if (rec != null) {
|
||||||
val declaring = when {
|
val declaring = when {
|
||||||
@ -109,7 +126,10 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 // do not default to objClass for outsiders
|
val caller = caller0 // do not default to objClass for outsiders
|
||||||
if (!canAccessMember(rec.visibility, declaring, caller))
|
if (!canAccessMember(rec.visibility, declaring, caller))
|
||||||
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${declaring?.className ?: "?"})").raise()
|
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.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
if (rec.value.assign(scope, newValue) == null)
|
if (rec.value.assign(scope, newValue) == null)
|
||||||
@ -119,14 +139,21 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
super.writeField(scope, name, newValue)
|
super.writeField(scope, name, newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments,
|
override suspend fun invokeInstanceMethod(
|
||||||
onNotFoundResult: (()->Obj?)?): Obj =
|
scope: Scope, name: String, args: Arguments,
|
||||||
|
onNotFoundResult: (() -> Obj?)?
|
||||||
|
): Obj =
|
||||||
instanceScope[name]?.let { rec ->
|
instanceScope[name]?.let { rec ->
|
||||||
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
|
val decl = rec.declaringClass ?: objClass.findDeclaringClassOf(name)
|
||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
|
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
|
||||||
if (!canAccessMember(rec.visibility, decl, caller))
|
if (!canAccessMember(rec.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
// execute with lexical class context propagated to declaring class
|
// execute with lexical class context propagated to declaring class
|
||||||
val saved = instanceScope.currentClassCtx
|
val saved = instanceScope.currentClassCtx
|
||||||
instanceScope.currentClassCtx = decl
|
instanceScope.currentClassCtx = decl
|
||||||
@ -134,7 +161,8 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
rec.value.invoke(
|
rec.value.invoke(
|
||||||
instanceScope,
|
instanceScope,
|
||||||
this,
|
this,
|
||||||
args)
|
args
|
||||||
|
)
|
||||||
} finally {
|
} finally {
|
||||||
instanceScope.currentClassCtx = saved
|
instanceScope.currentClassCtx = saved
|
||||||
}
|
}
|
||||||
@ -146,7 +174,12 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
val caller0 = scope.currentClassCtx ?: instanceScope.currentClassCtx
|
||||||
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
|
val caller = caller0 ?: if (scope.thisObj === this) objClass else null
|
||||||
if (!canAccessMember(rec.visibility, decl, caller))
|
if (!canAccessMember(rec.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
val saved = instanceScope.currentClassCtx
|
val saved = instanceScope.currentClassCtx
|
||||||
instanceScope.currentClassCtx = decl
|
instanceScope.currentClassCtx = decl
|
||||||
try {
|
try {
|
||||||
@ -181,20 +214,45 @@ class ObjInstance(override val objClass: ObjClass) : Obj() {
|
|||||||
serializeStateVars(scope, encoder)
|
serializeStateVars(scope, encoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected val instanceVars: Map<String, ObjRecord> by lazy {
|
override suspend fun toJson(scope: Scope): JsonElement {
|
||||||
|
// Call the class-provided map serializer:
|
||||||
|
val custom = invokeInstanceMethod(scope, "toJsonObject", Arguments.EMPTY, { ObjVoid })
|
||||||
|
if (custom != ObjVoid) {
|
||||||
|
// class json serializer returned something, so use it:
|
||||||
|
return custom.toJson(scope)
|
||||||
|
}
|
||||||
|
// no class serializer, serialize from constructor
|
||||||
|
val result = mutableMapOf<String, JsonElement>()
|
||||||
|
val meta = objClass.constructorMeta
|
||||||
|
?: scope.raiseError("can't serialize non-serializable object (no constructor meta)")
|
||||||
|
for (entry in meta.params)
|
||||||
|
result[entry.name] = readField(scope, entry.name).value.toJson(scope)
|
||||||
|
for (i in serializingVars)
|
||||||
|
result[i.key] = i.value.value.toJson(scope)
|
||||||
|
return JsonObject(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
val instanceVars: Map<String, ObjRecord> by lazy {
|
||||||
instanceScope.objects.filter { it.value.type.serializable }
|
instanceScope.objects.filter { it.value.type.serializable }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected suspend fun serializeStateVars(scope: Scope,encoder: LynonEncoder) {
|
val serializingVars: Map<String, ObjRecord> by lazy {
|
||||||
|
instanceScope.objects.filter {
|
||||||
|
it.value.type.serializable &&
|
||||||
|
it.value.type == ObjRecord.Type.Field &&
|
||||||
|
it.value.isMutable }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected suspend fun serializeStateVars(scope: Scope, encoder: LynonEncoder) {
|
||||||
val vars = instanceVars.values.map { it.value }
|
val vars = instanceVars.values.map { it.value }
|
||||||
if( vars.isNotEmpty()) {
|
if (vars.isNotEmpty()) {
|
||||||
encoder.encodeAnyList(scope, vars)
|
encoder.encodeAnyList(scope, vars)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun deserializeStateVars(scope: Scope, decoder: LynonDecoder) {
|
internal suspend fun deserializeStateVars(scope: Scope, decoder: LynonDecoder) {
|
||||||
val localVars = instanceVars.values.toList()
|
val localVars = instanceVars.values.toList()
|
||||||
if( localVars.isNotEmpty() ) {
|
if (localVars.isNotEmpty()) {
|
||||||
val vars = decoder.decodeAnyList(scope)
|
val vars = decoder.decodeAnyList(scope)
|
||||||
if (vars.size > instanceVars.size)
|
if (vars.size > instanceVars.size)
|
||||||
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
|
scope.raiseIllegalArgument("serialized vars has bigger size than instance vars")
|
||||||
@ -250,7 +308,12 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(rec.visibility, decl, caller))
|
if (!canAccessMember(rec.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't access field $name (declared in ${decl?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
return rec
|
return rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +324,15 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
if (!canAccessMember(r.visibility, decl, caller))
|
if (!canAccessMember(r.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
|
scope.raiseError(ObjAccessException(scope, "can't access field $name (declared in ${decl.className})"))
|
||||||
return when (val value = r.value) {
|
return when (val value = r.value) {
|
||||||
is net.sergeych.lyng.Statement -> ObjRecord(value.execute(instance.instanceScope.createChildScope(scope.pos, newThisObj = instance)), r.isMutable)
|
is net.sergeych.lyng.Statement -> ObjRecord(
|
||||||
|
value.execute(
|
||||||
|
instance.instanceScope.createChildScope(
|
||||||
|
scope.pos,
|
||||||
|
newThisObj = instance
|
||||||
|
)
|
||||||
|
), r.isMutable
|
||||||
|
)
|
||||||
|
|
||||||
else -> r
|
else -> r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,7 +344,10 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
val decl = f.declaringClass ?: startClass
|
val decl = f.declaringClass ?: startClass
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(f.visibility, decl, caller))
|
if (!canAccessMember(f.visibility, decl, caller))
|
||||||
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl.className})").raise()
|
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.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
if (f.value.assign(scope, newValue) == null) f.value = newValue
|
if (f.value.assign(scope, newValue) == null) f.value = newValue
|
||||||
return
|
return
|
||||||
@ -284,7 +358,10 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
val decl = f.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
val decl = f.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(f.visibility, decl, caller))
|
if (!canAccessMember(f.visibility, decl, caller))
|
||||||
ObjIllegalAssignmentException(scope, "can't assign to field $name (declared in ${decl?.className ?: "?"})").raise()
|
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.isMutable) ObjIllegalAssignmentException(scope, "can't reassign val $name").raise()
|
||||||
if (f.value.assign(scope, newValue) == null) f.value = newValue
|
if (f.value.assign(scope, newValue) == null) f.value = newValue
|
||||||
return
|
return
|
||||||
@ -299,7 +376,12 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
if (r.value.assign(scope, newValue) == null) r.value = newValue
|
if (r.value.assign(scope, newValue) == null) r.value = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun invokeInstanceMethod(scope: Scope, name: String, args: Arguments, onNotFoundResult: (() -> Obj?)?): Obj {
|
override suspend fun invokeInstanceMethod(
|
||||||
|
scope: Scope,
|
||||||
|
name: String,
|
||||||
|
args: Arguments,
|
||||||
|
onNotFoundResult: (() -> Obj?)?
|
||||||
|
): Obj {
|
||||||
// Qualified method dispatch must start from the specified ancestor, not from the instance scope.
|
// Qualified method dispatch must start from the specified ancestor, not from the instance scope.
|
||||||
memberFromAncestor(name)?.let { rec ->
|
memberFromAncestor(name)?.let { rec ->
|
||||||
val decl = rec.declaringClass ?: startClass
|
val decl = rec.declaringClass ?: startClass
|
||||||
@ -320,7 +402,12 @@ class ObjQualifiedView(val instance: ObjInstance, private val startClass: ObjCla
|
|||||||
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
val decl = rec.declaringClass ?: instance.objClass.findDeclaringClassOf(name)
|
||||||
val caller = scope.currentClassCtx
|
val caller = scope.currentClassCtx
|
||||||
if (!canAccessMember(rec.visibility, decl, caller))
|
if (!canAccessMember(rec.visibility, decl, caller))
|
||||||
scope.raiseError(ObjAccessException(scope, "can't invoke method $name (declared in ${decl?.className ?: "?"})"))
|
scope.raiseError(
|
||||||
|
ObjAccessException(
|
||||||
|
scope,
|
||||||
|
"can't invoke method $name (declared in ${decl?.className ?: "?"})"
|
||||||
|
)
|
||||||
|
)
|
||||||
val saved = instance.instanceScope.currentClassCtx
|
val saved = instance.instanceScope.currentClassCtx
|
||||||
instance.instanceScope.currentClassCtx = decl
|
instance.instanceScope.currentClassCtx = decl
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -3815,4 +3815,47 @@ class ScriptTest {
|
|||||||
assertEquals(JSTest1("bar", 1, true), x.decodeSerializable<JSTest1>())
|
assertEquals(JSTest1("bar", 1, true), x.decodeSerializable<JSTest1>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testInstanceVars() = runTest {
|
||||||
|
// val x = eval("""
|
||||||
|
// class T(x,y)
|
||||||
|
// T(1, 2)
|
||||||
|
// """.trimIndent()) as ObjInstance
|
||||||
|
// println(x.serializingVars.map { "${it.key}=${it.value.value}"})
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun memberValCantBeAssigned() = runTest {
|
||||||
|
eval("""
|
||||||
|
class Point(foo,bar) {
|
||||||
|
val t = 42
|
||||||
|
}
|
||||||
|
val p = Point(1,2)
|
||||||
|
// val should not be assignable:
|
||||||
|
assertThrows { p = Point(3,4) }
|
||||||
|
|
||||||
|
// val field must be readonly:
|
||||||
|
assertThrows { p.t = "bad" }
|
||||||
|
|
||||||
|
// and the value should not be changed
|
||||||
|
assertEqual(42, p.t)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testClassToJson() = runTest {
|
||||||
|
// val x = eval("""
|
||||||
|
// import lyng.serialization
|
||||||
|
// class Point(foo,bar) {
|
||||||
|
// val t = 42
|
||||||
|
// }
|
||||||
|
// val p = Point(1,2)
|
||||||
|
// p.t = 121
|
||||||
|
// println(Point(10,"bar").toJsonString())
|
||||||
|
// Lynon.encode(Point(1,2))
|
||||||
|
// """.trimIndent())
|
||||||
|
// println((x as ObjBitBuffer).bitArray.asUByteArray().toDump())
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -154,6 +154,7 @@ fun ensureLyngHighlightStyles() {
|
|||||||
.hl-class { color: #5a32a3; font-weight: 600; }
|
.hl-class { color: #5a32a3; font-weight: 600; }
|
||||||
.hl-val { color: #1b7f5a; }
|
.hl-val { color: #1b7f5a; }
|
||||||
.hl-var { color: #1b7f5a; text-decoration: underline dotted currentColor; }
|
.hl-var { color: #1b7f5a; text-decoration: underline dotted currentColor; }
|
||||||
|
.hl-enumc { color: #b08800; font-weight: 600; }
|
||||||
.hl-param { color: #0969da; font-style: italic; }
|
.hl-param { color: #0969da; font-style: italic; }
|
||||||
.hl-num { color: #005cc5; }
|
.hl-num { color: #005cc5; }
|
||||||
.hl-str { color: #032f62; }
|
.hl-str { color: #032f62; }
|
||||||
@ -177,6 +178,7 @@ fun ensureLyngHighlightStyles() {
|
|||||||
[data-bs-theme="dark"] .hl-class{ color: #d2a8ff; font-weight: 700; }
|
[data-bs-theme="dark"] .hl-class{ color: #d2a8ff; font-weight: 700; }
|
||||||
[data-bs-theme="dark"] .hl-val { color: #7ee787; }
|
[data-bs-theme="dark"] .hl-val { color: #7ee787; }
|
||||||
[data-bs-theme="dark"] .hl-var { color: #7ee787; text-decoration: underline dotted currentColor; }
|
[data-bs-theme="dark"] .hl-var { color: #7ee787; text-decoration: underline dotted currentColor; }
|
||||||
|
[data-bs-theme="dark"] .hl-enumc{ color: #f2cc60; font-weight: 700; }
|
||||||
[data-bs-theme="dark"] .hl-param{ color: #a5d6ff; font-style: italic; }
|
[data-bs-theme="dark"] .hl-param{ color: #a5d6ff; font-style: italic; }
|
||||||
[data-bs-theme="dark"] .hl-num { color: #79c0ff; }
|
[data-bs-theme="dark"] .hl-num { color: #79c0ff; }
|
||||||
[data-bs-theme="dark"] .hl-str,
|
[data-bs-theme="dark"] .hl-str,
|
||||||
@ -677,6 +679,7 @@ private fun cssClassForKind(kind: HighlightKind): String = when (kind) {
|
|||||||
HighlightKind.Label -> "hl-lbl"
|
HighlightKind.Label -> "hl-lbl"
|
||||||
HighlightKind.Directive -> "hl-dir"
|
HighlightKind.Directive -> "hl-dir"
|
||||||
HighlightKind.Error -> "hl-err"
|
HighlightKind.Error -> "hl-err"
|
||||||
|
HighlightKind.EnumConstant -> "hl-enumc"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user