Add list literal opcode and bytecode wrappers
This commit is contained in:
parent
aebe0890d8
commit
a4fc5ac6d5
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
|
||||
class ClassDeclStatement(
|
||||
private val delegate: Statement,
|
||||
private val startPos: Pos,
|
||||
) : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
return delegate.execute(scope)
|
||||
}
|
||||
}
|
||||
@ -251,6 +251,10 @@ class Compiler(
|
||||
val statements = mutableListOf<Statement>()
|
||||
val start = cc.currentPos()
|
||||
// Track locals at script level for fast local refs
|
||||
val needsSlotPlan = slotPlanStack.isEmpty()
|
||||
if (needsSlotPlan) {
|
||||
slotPlanStack.add(SlotPlan(mutableMapOf(), 0))
|
||||
}
|
||||
return try {
|
||||
withLocalNames(emptySet()) {
|
||||
// package level declarations
|
||||
@ -360,6 +364,9 @@ class Compiler(
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
if (needsSlotPlan) {
|
||||
slotPlanStack.removeLast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,9 +394,51 @@ class Compiler(
|
||||
private val currentRangeParamNames: Set<String>
|
||||
get() = rangeParamNamesStack.lastOrNull() ?: emptySet()
|
||||
|
||||
private fun containsLoopControl(stmt: Statement, inLoop: Boolean = false): Boolean {
|
||||
val target = if (stmt is BytecodeStatement) stmt.original else stmt
|
||||
return when (target) {
|
||||
is BreakStatement, is ContinueStatement -> !inLoop
|
||||
is IfStatement -> {
|
||||
containsLoopControl(target.ifBody, inLoop) ||
|
||||
(target.elseBody?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||
}
|
||||
is ForInStatement -> {
|
||||
containsLoopControl(target.body, true) ||
|
||||
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||
}
|
||||
is WhileStatement -> {
|
||||
containsLoopControl(target.body, true) ||
|
||||
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||
}
|
||||
is DoWhileStatement -> {
|
||||
containsLoopControl(target.body, true) ||
|
||||
(target.elseStatement?.let { containsLoopControl(it, inLoop) } ?: false)
|
||||
}
|
||||
is BlockStatement -> target.statements().any { containsLoopControl(it, inLoop) }
|
||||
is VarDeclStatement -> target.initializer?.let { containsLoopControl(it, inLoop) } ?: false
|
||||
is ReturnStatement, is ThrowStatement, is ExpressionStatement -> false
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapBytecode(stmt: Statement): Statement {
|
||||
if (!useBytecodeStatements) return stmt
|
||||
val allowLocals = codeContexts.lastOrNull() is CodeContext.Function
|
||||
if (codeContexts.lastOrNull() is CodeContext.ClassBody) {
|
||||
return stmt
|
||||
}
|
||||
if (stmt is FunctionDeclStatement ||
|
||||
stmt is ClassDeclStatement ||
|
||||
stmt is EnumDeclStatement ||
|
||||
stmt is BreakStatement ||
|
||||
stmt is ContinueStatement ||
|
||||
stmt is ReturnStatement
|
||||
) {
|
||||
return stmt
|
||||
}
|
||||
if (containsLoopControl(stmt)) {
|
||||
return stmt
|
||||
}
|
||||
val allowLocals = codeContexts.lastOrNull() !is CodeContext.ClassBody
|
||||
val returnLabels = returnLabelStack.lastOrNull() ?: emptySet()
|
||||
return BytecodeStatement.wrap(
|
||||
stmt,
|
||||
@ -738,11 +787,7 @@ class Compiler(
|
||||
isCall = true
|
||||
val lambda = parseLambdaExpression()
|
||||
val argPos = next.pos
|
||||
val argStmt = object : Statement() {
|
||||
override val pos: Pos = argPos
|
||||
override suspend fun execute(scope: Scope): Obj = lambda.get(scope).value
|
||||
}
|
||||
val args = listOf(ParsedArgument(argStmt, next.pos))
|
||||
val args = listOf(ParsedArgument(ExpressionStatement(lambda, argPos), next.pos))
|
||||
operand = when (left) {
|
||||
is LocalVarRef -> if (left.name == "this") {
|
||||
ThisMethodSlotCallRef(next.value, args, true, isOptional)
|
||||
@ -1421,11 +1466,7 @@ class Compiler(
|
||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||
val localVar = LocalVarRef(name, t1.pos)
|
||||
val argPos = t1.pos
|
||||
val argStmt = object : Statement() {
|
||||
override val pos: Pos = argPos
|
||||
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
||||
}
|
||||
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
||||
return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name)
|
||||
}
|
||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||
@ -1468,11 +1509,7 @@ class Compiler(
|
||||
// last argument - callable
|
||||
val callableAccessor = parseLambdaExpression()
|
||||
args += ParsedArgument(
|
||||
// transform ObjRef to the callable value
|
||||
object : Statement() {
|
||||
override val pos: Pos = end.pos
|
||||
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
||||
},
|
||||
ExpressionStatement(callableAccessor, end.pos),
|
||||
end.pos
|
||||
)
|
||||
lastBlockArgument = true
|
||||
@ -1499,11 +1536,7 @@ class Compiler(
|
||||
if (next.type == Token.Type.COMMA || next.type == Token.Type.RPAREN) {
|
||||
val localVar = LocalVarRef(name, t1.pos)
|
||||
val argPos = t1.pos
|
||||
val argStmt = object : Statement() {
|
||||
override val pos: Pos = argPos
|
||||
override suspend fun execute(scope: Scope): Obj = localVar.evalValue(scope)
|
||||
}
|
||||
return ParsedArgument(argStmt, t1.pos, isSplat = false, name = name)
|
||||
return ParsedArgument(ExpressionStatement(localVar, argPos), t1.pos, isSplat = false, name = name)
|
||||
}
|
||||
val rhs = parseExpression() ?: t2.raiseSyntax("expected expression after named argument '${name}:'")
|
||||
return ParsedArgument(rhs, t1.pos, isSplat = false, name = name)
|
||||
@ -1555,11 +1588,7 @@ class Compiler(
|
||||
// into the lambda body. This ensures expected order:
|
||||
// foo { ... }.bar() == (foo { ... }).bar()
|
||||
val callableAccessor = parseLambdaExpression()
|
||||
val argStmt = object : Statement() {
|
||||
override val pos: Pos = cc.currentPos()
|
||||
override suspend fun execute(scope: Scope): Obj = callableAccessor.get(scope).value
|
||||
}
|
||||
listOf(ParsedArgument(argStmt, cc.currentPos()))
|
||||
listOf(ParsedArgument(ExpressionStatement(callableAccessor, cc.currentPos()), cc.currentPos()))
|
||||
} else {
|
||||
val r = parseArgs()
|
||||
detectedBlockArgument = r.second
|
||||
@ -2320,7 +2349,7 @@ class Compiler(
|
||||
)
|
||||
|
||||
val stmtPos = startPos
|
||||
return object : Statement() {
|
||||
val enumDeclStatement = object : Statement() {
|
||||
override val pos: Pos = stmtPos
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
val enumClass = ObjEnumClass.createSimpleEnum(nameToken.value, names)
|
||||
@ -2328,6 +2357,7 @@ class Compiler(
|
||||
return enumClass
|
||||
}
|
||||
}
|
||||
return EnumDeclStatement(enumDeclStatement, stmtPos)
|
||||
}
|
||||
|
||||
private suspend fun parseObjectDeclaration(isExtern: Boolean = false): Statement {
|
||||
@ -2586,7 +2616,7 @@ class Compiler(
|
||||
return instance
|
||||
}
|
||||
}
|
||||
object : Statement() {
|
||||
val classDeclStatement = object : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
// the main statement should create custom ObjClass instance with field
|
||||
@ -2643,6 +2673,7 @@ class Compiler(
|
||||
return newClass
|
||||
}
|
||||
}
|
||||
ClassDeclStatement(classDeclStatement, startPos)
|
||||
}
|
||||
|
||||
}
|
||||
@ -3279,11 +3310,12 @@ class Compiler(
|
||||
return annotatedFnBody
|
||||
}
|
||||
}
|
||||
val declaredFn = FunctionDeclStatement(fnCreateStatement, start)
|
||||
if (isStatic) {
|
||||
currentInitScope += fnCreateStatement
|
||||
currentInitScope += declaredFn
|
||||
NopStatement
|
||||
} else
|
||||
fnCreateStatement
|
||||
declaredFn
|
||||
}.also {
|
||||
val bodyRange = lastParsedBlockRange
|
||||
// Also emit a post-parse MiniFunDecl to be robust in case early emission was skipped by some path
|
||||
@ -3823,43 +3855,26 @@ class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
return object : Statement() {
|
||||
override val pos: Pos = start
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
if (extTypeName != null) {
|
||||
val prop = if (getter != null || setter != null) {
|
||||
ObjProperty(name, getter, setter)
|
||||
} else {
|
||||
// Simple val extension with initializer
|
||||
val initExpr = initialExpression ?: throw ScriptError(start, "Extension val must be initialized")
|
||||
ObjProperty(
|
||||
name,
|
||||
object : Statement() {
|
||||
override val pos: Pos = initExpr.pos
|
||||
override suspend fun execute(scp: Scope): Obj = initExpr.execute(scp)
|
||||
},
|
||||
null
|
||||
)
|
||||
ObjProperty(name, initExpr, null)
|
||||
}
|
||||
|
||||
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
|
||||
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
|
||||
|
||||
context.addExtension(
|
||||
type,
|
||||
name,
|
||||
ObjRecord(
|
||||
prop,
|
||||
isMutable = false,
|
||||
return ExtensionPropertyDeclStatement(
|
||||
extTypeName = extTypeName,
|
||||
property = prop,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
declaringClass = null,
|
||||
type = ObjRecord.Type.Property
|
||||
setterVisibility = setterVisibility,
|
||||
startPos = start
|
||||
)
|
||||
)
|
||||
|
||||
return prop
|
||||
}
|
||||
|
||||
return object : Statement() {
|
||||
override val pos: Pos = start
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
// In true class bodies (not inside a function), store fields under a class-qualified key to support MI collisions
|
||||
// Do NOT infer declaring class from runtime thisObj here; only the compile-time captured
|
||||
// ClassBody qualifies for class-field storage. Otherwise, this is a plain local.
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
|
||||
class EnumDeclStatement(
|
||||
private val delegate: Statement,
|
||||
private val startPos: Pos,
|
||||
) : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
return delegate.execute(scope)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjProperty
|
||||
import net.sergeych.lyng.obj.ObjRecord
|
||||
|
||||
class ExtensionPropertyDeclStatement(
|
||||
val extTypeName: String,
|
||||
val property: ObjProperty,
|
||||
val visibility: Visibility,
|
||||
val setterVisibility: Visibility?,
|
||||
private val startPos: Pos,
|
||||
) : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(context: Scope): Obj {
|
||||
val type = context[extTypeName]?.value ?: context.raiseSymbolNotFound("class $extTypeName not found")
|
||||
if (type !is ObjClass) context.raiseClassCastError("$extTypeName is not the class instance")
|
||||
context.addExtension(
|
||||
type,
|
||||
property.name,
|
||||
ObjRecord(
|
||||
property,
|
||||
isMutable = false,
|
||||
visibility = visibility,
|
||||
writeVisibility = setterVisibility,
|
||||
declaringClass = null,
|
||||
type = ObjRecord.Type.Property
|
||||
)
|
||||
)
|
||||
return property
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2026 Sergey S. Chernov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.sergeych.lyng
|
||||
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
|
||||
class FunctionDeclStatement(
|
||||
private val delegate: Statement,
|
||||
private val startPos: Pos,
|
||||
) : Statement() {
|
||||
override val pos: Pos = startPos
|
||||
|
||||
override suspend fun execute(scope: Scope): Obj {
|
||||
return delegate.execute(scope)
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,7 @@ sealed class BytecodeConst {
|
||||
data class ObjRef(val value: Obj) : BytecodeConst()
|
||||
data class Ref(val value: net.sergeych.lyng.obj.ObjRef) : BytecodeConst()
|
||||
data class StatementVal(val statement: net.sergeych.lyng.Statement) : BytecodeConst()
|
||||
data class ListLiteralPlan(val spreads: List<Boolean>) : BytecodeConst()
|
||||
data class SlotPlan(val plan: Map<String, Int>) : BytecodeConst()
|
||||
data class ExtensionPropertyDecl(
|
||||
val extTypeName: String,
|
||||
|
||||
@ -100,6 +100,7 @@ class BytecodeStatement private constructor(
|
||||
target.resultExpr?.let { containsUnsupportedStatement(it) } ?: false
|
||||
is net.sergeych.lyng.ThrowStatement ->
|
||||
containsUnsupportedStatement(target.throwExpr)
|
||||
is net.sergeych.lyng.ExtensionPropertyDeclStatement -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +182,8 @@ class CmdBuilder {
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.SET_INDEX ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.LIST_LITERAL ->
|
||||
listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
||||
listOf(OperandKind.ID, OperandKind.SLOT)
|
||||
}
|
||||
@ -371,6 +373,7 @@ class CmdBuilder {
|
||||
Opcode.GET_NAME -> CmdGetName(operands[0], operands[1])
|
||||
Opcode.GET_INDEX -> CmdGetIndex(operands[0], operands[1], operands[2])
|
||||
Opcode.SET_INDEX -> CmdSetIndex(operands[0], operands[1], operands[2])
|
||||
Opcode.LIST_LITERAL -> CmdListLiteral(operands[0], operands[1], operands[2], operands[3])
|
||||
Opcode.EVAL_FALLBACK -> CmdEvalFallback(operands[0], operands[1])
|
||||
Opcode.EVAL_REF -> CmdEvalRef(operands[0], operands[1])
|
||||
Opcode.EVAL_STMT -> CmdEvalStmt(operands[0], operands[1])
|
||||
|
||||
@ -183,6 +183,7 @@ object CmdDisassembler {
|
||||
is CmdGetName -> Opcode.GET_NAME to intArrayOf(cmd.nameId, cmd.dst)
|
||||
is CmdGetIndex -> Opcode.GET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.dst)
|
||||
is CmdSetIndex -> Opcode.SET_INDEX to intArrayOf(cmd.targetSlot, cmd.indexSlot, cmd.valueSlot)
|
||||
is CmdListLiteral -> Opcode.LIST_LITERAL to intArrayOf(cmd.planId, cmd.baseSlot, cmd.count, cmd.dst)
|
||||
is CmdEvalFallback -> Opcode.EVAL_FALLBACK to intArrayOf(cmd.id, cmd.dst)
|
||||
is CmdEvalRef -> Opcode.EVAL_REF to intArrayOf(cmd.id, cmd.dst)
|
||||
is CmdEvalStmt -> Opcode.EVAL_STMT to intArrayOf(cmd.id, cmd.dst)
|
||||
@ -265,6 +266,8 @@ object CmdDisassembler {
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.SET_INDEX ->
|
||||
listOf(OperandKind.SLOT, OperandKind.SLOT, OperandKind.SLOT)
|
||||
Opcode.LIST_LITERAL ->
|
||||
listOf(OperandKind.CONST, OperandKind.SLOT, OperandKind.COUNT, OperandKind.SLOT)
|
||||
Opcode.EVAL_FALLBACK, Opcode.EVAL_REF, Opcode.EVAL_STMT ->
|
||||
listOf(OperandKind.ID, OperandKind.SLOT)
|
||||
}
|
||||
|
||||
@ -1223,6 +1223,35 @@ class CmdGetName(
|
||||
}
|
||||
}
|
||||
|
||||
class CmdListLiteral(
|
||||
internal val planId: Int,
|
||||
internal val baseSlot: Int,
|
||||
internal val count: Int,
|
||||
internal val dst: Int,
|
||||
) : Cmd() {
|
||||
override suspend fun perform(frame: CmdFrame) {
|
||||
val plan = frame.fn.constants.getOrNull(planId) as? BytecodeConst.ListLiteralPlan
|
||||
?: error("LIST_LITERAL expects ListLiteralPlan at $planId")
|
||||
val list = ArrayList<Obj>(count)
|
||||
for (i in 0 until count) {
|
||||
val value = frame.slotToObj(baseSlot + i)
|
||||
if (plan.spreads.getOrNull(i) == true) {
|
||||
when (value) {
|
||||
is ObjList -> {
|
||||
list.ensureCapacity(list.size + value.list.size)
|
||||
list.addAll(value.list)
|
||||
}
|
||||
else -> frame.scope.raiseError("Spread element must be list")
|
||||
}
|
||||
} else {
|
||||
list.add(value)
|
||||
}
|
||||
}
|
||||
frame.storeObjResult(dst, ObjList(list))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
class CmdSetField(
|
||||
internal val recvSlot: Int,
|
||||
internal val fieldId: Int,
|
||||
|
||||
@ -130,6 +130,7 @@ enum class Opcode(val code: Int) {
|
||||
GET_INDEX(0xA2),
|
||||
SET_INDEX(0xA3),
|
||||
GET_NAME(0xA4),
|
||||
LIST_LITERAL(0xA5),
|
||||
|
||||
EVAL_FALLBACK(0xB0),
|
||||
RESOLVE_SCOPE_SLOT(0xB1),
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.eval
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
|
||||
@Ignore("TODO(bytecode-only): uses fallback")
|
||||
class IfNullAssignTest {
|
||||
|
||||
@Test
|
||||
|
||||
@ -21,10 +21,8 @@
|
||||
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import net.sergeych.lyng.eval
|
||||
import kotlin.test.Ignore
|
||||
import kotlin.test.Test
|
||||
|
||||
@Ignore("TODO(bytecode-only): uses fallback")
|
||||
class ScriptTest_OptionalAssign {
|
||||
|
||||
@Test
|
||||
|
||||
@ -249,20 +249,6 @@ fun List.sort() {
|
||||
sortWith { a, b -> a <=> b }
|
||||
}
|
||||
|
||||
/* Represents a single stack trace element. */
|
||||
class StackTraceEntry(
|
||||
val sourceName: String,
|
||||
val line: Int,
|
||||
val column: Int,
|
||||
val sourceString: String
|
||||
) {
|
||||
val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) }
|
||||
/* Formatted representation: source:line:column: text. */
|
||||
override fun toString() {
|
||||
"%s: %s"(at, sourceString.trim())
|
||||
}
|
||||
}
|
||||
|
||||
/* Print this exception and its stack trace to standard output. */
|
||||
fun Exception.printStackTrace() {
|
||||
println(this)
|
||||
@ -337,3 +323,17 @@ class lazy(creatorParam) : Delegate {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
/* Represents a single stack trace element. */
|
||||
class StackTraceEntry(
|
||||
val sourceName: String,
|
||||
val line: Int,
|
||||
val column: Int,
|
||||
val sourceString: String
|
||||
) {
|
||||
val at by lazy { "%s:%s:%s"(sourceName,line+1,column+1) }
|
||||
/* Formatted representation: source:line:column: text. */
|
||||
override fun toString() {
|
||||
"%s: %s"(at, sourceString.trim())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user