Fix JVM import caching and class/object bytecode dispatch
This commit is contained in:
parent
953f237ca3
commit
0973a6afeb
15
examples/fillspeed.lyng
Normal file
15
examples/fillspeed.lyng
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import lyng.time
|
||||||
|
|
||||||
|
val n = 700_000
|
||||||
|
|
||||||
|
fun tm<T>(block: ()->T): T {
|
||||||
|
val t = Instant()
|
||||||
|
block().also {
|
||||||
|
println("tm: ${Instant() - t}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = tm { List.fill(n) { it * 10 + 1 } }
|
||||||
|
val y = tm { List.fill(n, n + 10) { it * 10 + 1 } }
|
||||||
|
tm { x.add(-1) }
|
||||||
|
tm { y.add(-2) }
|
||||||
@ -1948,7 +1948,7 @@ class Compiler(
|
|||||||
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
||||||
knownNameObjClass = knownClassMapForBytecode(),
|
knownNameObjClass = knownClassMapForBytecode(),
|
||||||
knownClassNames = knownClassNamesForBytecode(),
|
knownClassNames = knownClassNamesForBytecode(),
|
||||||
knownObjectNames = objectDeclNames,
|
knownObjectNames = knownObjectNamesForBytecode(),
|
||||||
classFieldTypesByName = classFieldTypesByName,
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
@ -2277,6 +2277,23 @@ class Compiler(
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun knownObjectNamesForBytecode(): Set<String> {
|
||||||
|
val result = LinkedHashSet<String>()
|
||||||
|
fun addScope(scope: Scope?) {
|
||||||
|
if (scope == null) return
|
||||||
|
for ((name, rec) in scope.objects) {
|
||||||
|
if (rec.value is ObjInstance) result.add(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addScope(seedScope)
|
||||||
|
addScope(importManager.rootScope)
|
||||||
|
for (module in importedModules) {
|
||||||
|
addScope(module.scope)
|
||||||
|
}
|
||||||
|
result.addAll(objectDeclNames)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun wrapBytecode(stmt: Statement): Statement {
|
private fun wrapBytecode(stmt: Statement): Statement {
|
||||||
if (codeContexts.lastOrNull() is CodeContext.Module) return stmt
|
if (codeContexts.lastOrNull() is CodeContext.Module) return stmt
|
||||||
if (codeContexts.lastOrNull() is CodeContext.ClassBody) return stmt
|
if (codeContexts.lastOrNull() is CodeContext.ClassBody) return stmt
|
||||||
@ -2305,7 +2322,7 @@ class Compiler(
|
|||||||
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
||||||
knownNameObjClass = knownClassMapForBytecode(),
|
knownNameObjClass = knownClassMapForBytecode(),
|
||||||
knownClassNames = knownClassNamesForBytecode(),
|
knownClassNames = knownClassNamesForBytecode(),
|
||||||
knownObjectNames = objectDeclNames,
|
knownObjectNames = knownObjectNamesForBytecode(),
|
||||||
classFieldTypesByName = classFieldTypesByName,
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
@ -2340,7 +2357,7 @@ class Compiler(
|
|||||||
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
||||||
knownNameObjClass = knownClassMapForBytecode(),
|
knownNameObjClass = knownClassMapForBytecode(),
|
||||||
knownClassNames = knownClassNamesForBytecode(),
|
knownClassNames = knownClassNamesForBytecode(),
|
||||||
knownObjectNames = objectDeclNames,
|
knownObjectNames = knownObjectNamesForBytecode(),
|
||||||
classFieldTypesByName = classFieldTypesByName,
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
@ -2400,7 +2417,7 @@ class Compiler(
|
|||||||
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
slotTypeDeclByScopeId = slotTypeDeclByScopeId,
|
||||||
knownNameObjClass = knownNames,
|
knownNameObjClass = knownNames,
|
||||||
knownClassNames = knownClassNamesForBytecode(),
|
knownClassNames = knownClassNamesForBytecode(),
|
||||||
knownObjectNames = objectDeclNames,
|
knownObjectNames = knownObjectNamesForBytecode(),
|
||||||
classFieldTypesByName = classFieldTypesByName,
|
classFieldTypesByName = classFieldTypesByName,
|
||||||
enumEntriesByName = enumEntriesByName,
|
enumEntriesByName = enumEntriesByName,
|
||||||
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
callableReturnTypeByScopeId = callableReturnTypeByScopeId,
|
||||||
|
|||||||
@ -2688,9 +2688,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
if ((isKnownClassReceiver(target.target) || isClassNameRef(target.target, receiverClass)) &&
|
if (shouldUseClassScopeReceiver(target.target, receiver, receiverClass)) {
|
||||||
(isClassSlot(receiver.slot) || receiverClass == ObjClassType)
|
|
||||||
) {
|
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(target.name))
|
||||||
if (!target.isOptional) {
|
if (!target.isOptional) {
|
||||||
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot)
|
builder.emit(Opcode.SET_CLASS_SCOPE, receiver.slot, nameId, value.slot)
|
||||||
@ -3050,7 +3048,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val fieldId = if (resolvedMember != null) receiverClass.instanceFieldIdMap()[fieldTarget.name] else null
|
val fieldId = if (resolvedMember != null) receiverClass.instanceFieldIdMap()[fieldTarget.name] else null
|
||||||
val methodId = if (resolvedMember != null) receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name] else null
|
val methodId = if (resolvedMember != null) receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name] else null
|
||||||
if (fieldId == null && methodId == null && (receiverClass == ObjClassType || isKnownClassReceiver(fieldTarget.target) || isClassNameRef(fieldTarget.target, receiverClass))) {
|
if (fieldId == null && methodId == null && shouldUseClassScopeReceiver(fieldTarget.target, receiver, receiverClass)) {
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
||||||
if (!fieldTarget.isOptional) {
|
if (!fieldTarget.isOptional) {
|
||||||
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current)
|
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, current)
|
||||||
@ -3564,9 +3562,7 @@ class BytecodeCompiler(
|
|||||||
val encodedMethodId = encodeMemberId(receiverClass, methodId)
|
val encodedMethodId = encodeMemberId(receiverClass, methodId)
|
||||||
val receiver = compileRefWithFallback(ref.target, null, pos) ?: return null
|
val receiver = compileRefWithFallback(ref.target, null, pos) ?: return null
|
||||||
val dst = allocSlot()
|
val dst = allocSlot()
|
||||||
if (fieldId == null && methodId == null && (isKnownClassReceiver(ref.target) || isClassNameRef(ref.target, receiverClass)) &&
|
if (fieldId == null && methodId == null && shouldUseClassScopeReceiver(ref.target, receiver, receiverClass)) {
|
||||||
(isClassSlot(receiver.slot) || receiverClass == ObjClassType)
|
|
||||||
) {
|
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||||
if (!ref.isOptional) {
|
if (!ref.isOptional) {
|
||||||
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst)
|
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, dst)
|
||||||
@ -4244,9 +4240,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name]
|
val fieldId = receiverClass.instanceFieldIdMap()[fieldTarget.name]
|
||||||
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name]
|
val methodId = receiverClass.instanceMethodIdMap(includeAbstract = true)[fieldTarget.name]
|
||||||
if (fieldId == null && methodId == null && (isKnownClassReceiver(fieldTarget.target) || isClassNameRef(fieldTarget.target, receiverClass)) &&
|
if (fieldId == null && methodId == null && shouldUseClassScopeReceiver(fieldTarget.target, receiver, receiverClass)) {
|
||||||
(isClassSlot(receiver.slot) || receiverClass == ObjClassType)
|
|
||||||
) {
|
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
val nameId = builder.addConst(BytecodeConst.StringVal(fieldTarget.name))
|
||||||
val resultSlot = allocSlot()
|
val resultSlot = allocSlot()
|
||||||
if (fieldTarget.isOptional) {
|
if (fieldTarget.isOptional) {
|
||||||
@ -4806,6 +4800,34 @@ class BytecodeCompiler(
|
|||||||
val receiverClass = resolveReceiverClass(ref.receiver) ?: ObjDynamic.type
|
val receiverClass = resolveReceiverClass(ref.receiver) ?: ObjDynamic.type
|
||||||
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
|
val receiver = compileRefWithFallback(ref.receiver, null, refPosOrCurrent(ref.receiver)) ?: return null
|
||||||
val dst = allocSlot()
|
val dst = allocSlot()
|
||||||
|
if (shouldUseClassScopeReceiver(ref.receiver, receiver, receiverClass)) {
|
||||||
|
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
||||||
|
val memberSlot = allocSlot()
|
||||||
|
if (!ref.isOptional) {
|
||||||
|
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
|
||||||
|
} else {
|
||||||
|
val nullSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CONST_NULL, nullSlot)
|
||||||
|
val cmpSlot = allocSlot()
|
||||||
|
builder.emit(Opcode.CMP_REF_EQ_OBJ, receiver.slot, nullSlot, cmpSlot)
|
||||||
|
val nullLabel = builder.label()
|
||||||
|
val endLabel = builder.label()
|
||||||
|
builder.emit(
|
||||||
|
Opcode.JMP_IF_TRUE,
|
||||||
|
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
|
||||||
|
)
|
||||||
|
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
|
||||||
|
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
||||||
|
builder.mark(nullLabel)
|
||||||
|
builder.emit(Opcode.CONST_NULL, memberSlot)
|
||||||
|
builder.mark(endLabel)
|
||||||
|
}
|
||||||
|
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||||
|
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||||
|
setPos(callPos)
|
||||||
|
emitCallCompiled(CompiledValue(memberSlot, SlotType.OBJ), args.base, encodedCount, dst)
|
||||||
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
|
}
|
||||||
fun emitDynamicCall(): CompiledValue? {
|
fun emitDynamicCall(): CompiledValue? {
|
||||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
||||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
val encodedCount = encodeCallArgCount(args) ?: return null
|
||||||
@ -4918,34 +4940,6 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
return CompiledValue(dst, SlotType.OBJ)
|
||||||
}
|
}
|
||||||
if (isKnownClassReceiver(ref.receiver)) {
|
|
||||||
val nameId = builder.addConst(BytecodeConst.StringVal(ref.name))
|
|
||||||
val memberSlot = allocSlot()
|
|
||||||
if (!ref.isOptional) {
|
|
||||||
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
|
|
||||||
} else {
|
|
||||||
val nullSlot = allocSlot()
|
|
||||||
builder.emit(Opcode.CONST_NULL, nullSlot)
|
|
||||||
val cmpSlot = allocSlot()
|
|
||||||
builder.emit(Opcode.CMP_REF_EQ_OBJ, receiver.slot, nullSlot, cmpSlot)
|
|
||||||
val nullLabel = builder.label()
|
|
||||||
val endLabel = builder.label()
|
|
||||||
builder.emit(
|
|
||||||
Opcode.JMP_IF_TRUE,
|
|
||||||
listOf(CmdBuilder.Operand.IntVal(cmpSlot), CmdBuilder.Operand.LabelRef(nullLabel))
|
|
||||||
)
|
|
||||||
builder.emit(Opcode.GET_CLASS_SCOPE, receiver.slot, nameId, memberSlot)
|
|
||||||
builder.emit(Opcode.JMP, listOf(CmdBuilder.Operand.LabelRef(endLabel)))
|
|
||||||
builder.mark(nullLabel)
|
|
||||||
builder.emit(Opcode.CONST_NULL, memberSlot)
|
|
||||||
builder.mark(endLabel)
|
|
||||||
}
|
|
||||||
val args = compileCallArgs(ref.args, ref.tailBlock, ref.explicitTypeArgs) ?: return null
|
|
||||||
val encodedCount = encodeCallArgCount(args) ?: return null
|
|
||||||
setPos(callPos)
|
|
||||||
emitCallCompiled(CompiledValue(memberSlot, SlotType.OBJ), args.base, encodedCount, dst)
|
|
||||||
return CompiledValue(dst, SlotType.OBJ)
|
|
||||||
}
|
|
||||||
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
val extSlot = resolveExtensionCallableSlot(receiverClass, ref.name)
|
||||||
?: throw BytecodeCompileException(
|
?: throw BytecodeCompileException(
|
||||||
missingMemberMessage(receiverClass, ref.name),
|
missingMemberMessage(receiverClass, ref.name),
|
||||||
@ -5544,7 +5538,7 @@ class BytecodeCompiler(
|
|||||||
if (scopeSlotCount > 0) {
|
if (scopeSlotCount > 0) {
|
||||||
for (index in 0 until scopeSlotCount) {
|
for (index in 0 until scopeSlotCount) {
|
||||||
val name = scopeSlotNames.getOrNull(index) ?: continue
|
val name = scopeSlotNames.getOrNull(index) ?: continue
|
||||||
resolveTypeNameClass(name)?.let { trackExactCallableObjAtSlot(index, it) }
|
preloadExactCallableName(index, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!allowLocalSlots || localSlotNames.isEmpty()) return
|
if (!allowLocalSlots || localSlotNames.isEmpty()) return
|
||||||
@ -5552,12 +5546,17 @@ class BytecodeCompiler(
|
|||||||
val name = localSlotNames[localIndex] ?: continue
|
val name = localSlotNames[localIndex] ?: continue
|
||||||
val key = localSlotKeyByIndex.getOrNull(localIndex)
|
val key = localSlotKeyByIndex.getOrNull(localIndex)
|
||||||
val isModuleLocal = key != null && moduleScopeId != null && key.scopeId == moduleScopeId
|
val isModuleLocal = key != null && moduleScopeId != null && key.scopeId == moduleScopeId
|
||||||
val isCapture = localSlotCaptures.getOrNull(localIndex) == true
|
val isCapture = key != null && captureSlotKeys.contains(key)
|
||||||
if (!isModuleLocal && !isCapture) continue
|
if (!isModuleLocal && !isCapture) continue
|
||||||
resolveTypeNameClass(name)?.let { trackExactCallableObjAtSlot(scopeSlotCount + localIndex, it) }
|
preloadExactCallableName(scopeSlotCount + localIndex, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun preloadExactCallableName(slot: Int, name: String) {
|
||||||
|
if (slotObjClass[slot] != ObjClassType && callSignatureByName[name] == null) return
|
||||||
|
resolveTypeNameClass(name)?.let { trackExactCallableObjAtSlot(slot, it) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun collectExactLambdaModuleCaptures() {
|
private fun collectExactLambdaModuleCaptures() {
|
||||||
if (exactLambdaRefByScopeId.isEmpty()) return
|
if (exactLambdaRefByScopeId.isEmpty()) return
|
||||||
val seen = LinkedHashSet<LambdaFnRef>()
|
val seen = LinkedHashSet<LambdaFnRef>()
|
||||||
@ -8324,39 +8323,69 @@ class BytecodeCompiler(
|
|||||||
return when (ref) {
|
return when (ref) {
|
||||||
is LocalVarRef -> {
|
is LocalVarRef -> {
|
||||||
val name = ref.name
|
val name = ref.name
|
||||||
if (nameObjClass[name] == ObjClassType) return true
|
|
||||||
val directSlot = resolveDirectNameSlot(name)?.slot
|
val directSlot = resolveDirectNameSlot(name)?.slot
|
||||||
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
|
if (directSlot != null) return isNamedClassBinding(name, directSlot)
|
||||||
|
if (nameObjClass[name] == ObjClassType) return true
|
||||||
if (localSlotIndexByName.containsKey(name)) return false
|
if (localSlotIndexByName.containsKey(name)) return false
|
||||||
if (localSlotInfoMap.values.any { it.name == name }) return false
|
if (localSlotInfoMap.values.any { it.name == name }) return false
|
||||||
(knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
|
isUnboundClassName(name)
|
||||||
!knownObjectNames.contains(name)
|
|
||||||
}
|
}
|
||||||
is LocalSlotRef -> {
|
is LocalSlotRef -> {
|
||||||
val name = ref.name
|
val name = ref.name
|
||||||
if (slotObjClass[ref.slot] == ObjClassType) return true
|
if (slotObjClass[ref.slot] != null || exactCallableObjBySlot[ref.slot] != null) {
|
||||||
if (nameObjClass[name] == ObjClassType) return true
|
return isNamedClassBinding(name, ref.slot)
|
||||||
|
}
|
||||||
val directSlot = resolveDirectNameSlot(name)?.slot
|
val directSlot = resolveDirectNameSlot(name)?.slot
|
||||||
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
|
if (directSlot != null) return isNamedClassBinding(name, directSlot)
|
||||||
|
if (nameObjClass[name] == ObjClassType) return true
|
||||||
if (localSlotIndexByName.containsKey(name)) return false
|
if (localSlotIndexByName.containsKey(name)) return false
|
||||||
if (localSlotInfoMap.values.any { it.name == name }) return false
|
if (localSlotInfoMap.values.any { it.name == name }) return false
|
||||||
(knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
|
isUnboundClassName(name)
|
||||||
!knownObjectNames.contains(name)
|
|
||||||
}
|
}
|
||||||
is FastLocalVarRef -> {
|
is FastLocalVarRef -> {
|
||||||
val name = ref.name
|
val name = ref.name
|
||||||
if (nameObjClass[name] == ObjClassType) return true
|
|
||||||
val directSlot = resolveDirectNameSlot(name)?.slot
|
val directSlot = resolveDirectNameSlot(name)?.slot
|
||||||
if (directSlot != null && slotObjClass[directSlot] == ObjClassType) return true
|
if (directSlot != null) return isNamedClassBinding(name, directSlot)
|
||||||
|
if (nameObjClass[name] == ObjClassType) return true
|
||||||
if (localSlotIndexByName.containsKey(name)) return false
|
if (localSlotIndexByName.containsKey(name)) return false
|
||||||
if (localSlotInfoMap.values.any { it.name == name }) return false
|
if (localSlotInfoMap.values.any { it.name == name }) return false
|
||||||
(knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)) &&
|
isUnboundClassName(name)
|
||||||
!knownObjectNames.contains(name)
|
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldUseClassScopeReceiver(
|
||||||
|
ref: ObjRef,
|
||||||
|
receiver: CompiledValue,
|
||||||
|
receiverClass: ObjClass
|
||||||
|
): Boolean {
|
||||||
|
val name = when (ref) {
|
||||||
|
is LocalVarRef -> ref.name
|
||||||
|
is LocalSlotRef -> ref.name
|
||||||
|
is FastLocalVarRef -> ref.name
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (name != null && isKnownObjectBinding(name)) return false
|
||||||
|
return isKnownClassReceiver(ref) &&
|
||||||
|
(isClassSlot(receiver.slot) || receiverClass == ObjClassType || isClassNameRef(ref, receiverClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isNamedClassBinding(name: String, slot: Int): Boolean {
|
||||||
|
if (isClassSlot(slot)) return true
|
||||||
|
if (isKnownObjectBinding(name)) return false
|
||||||
|
return knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isUnboundClassName(name: String): Boolean {
|
||||||
|
if (isKnownObjectBinding(name)) return false
|
||||||
|
return knownClassNames.contains(name) || classFieldTypesByName.containsKey(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isKnownObjectBinding(name: String): Boolean {
|
||||||
|
return knownObjectNames.contains(name)
|
||||||
|
}
|
||||||
|
|
||||||
private fun isClassNameRef(ref: ObjRef, receiverClass: ObjClass): Boolean {
|
private fun isClassNameRef(ref: ObjRef, receiverClass: ObjClass): Boolean {
|
||||||
if (receiverClass !is ObjInstanceClass) return false
|
if (receiverClass !is ObjInstanceClass) return false
|
||||||
val name = when (ref) {
|
val name = when (ref) {
|
||||||
@ -8365,10 +8394,11 @@ class BytecodeCompiler(
|
|||||||
is FastLocalVarRef -> ref.name
|
is FastLocalVarRef -> ref.name
|
||||||
else -> return false
|
else -> return false
|
||||||
}
|
}
|
||||||
return name == receiverClass.className
|
return name == receiverClass.className && isKnownClassReceiver(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isClassSlot(slot: Int): Boolean = slotObjClass[slot] == ObjClassType
|
private fun isClassSlot(slot: Int): Boolean =
|
||||||
|
slotObjClass[slot] == ObjClassType || exactCallableObjBySlot[slot] is ObjClass
|
||||||
|
|
||||||
|
|
||||||
private fun isThisReceiver(ref: ObjRef): Boolean {
|
private fun isThisReceiver(ref: ObjRef): Boolean {
|
||||||
@ -9320,8 +9350,10 @@ class BytecodeCompiler(
|
|||||||
val localIndex = localSlotIndexByKey[key] ?: continue
|
val localIndex = localSlotIndexByKey[key] ?: continue
|
||||||
val slot = scopeSlotCount + localIndex
|
val slot = scopeSlotCount + localIndex
|
||||||
localSlotCaptures[localIndex] = true
|
localSlotCaptures[localIndex] = true
|
||||||
forcedObjSlots.add(slot)
|
if (!slotTypes.containsKey(slot)) {
|
||||||
slotTypes[slot] = SlotType.OBJ
|
forcedObjSlots.add(slot)
|
||||||
|
slotTypes[slot] = SlotType.OBJ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allowLocalSlots && valueFnRefs.isNotEmpty() && lambdaCaptureEntriesByRef.isNotEmpty()) {
|
if (allowLocalSlots && valueFnRefs.isNotEmpty() && lambdaCaptureEntriesByRef.isNotEmpty()) {
|
||||||
@ -9349,8 +9381,10 @@ class BytecodeCompiler(
|
|||||||
val localIndex = localSlotIndexByName[name] ?: continue
|
val localIndex = localSlotIndexByName[name] ?: continue
|
||||||
val slot = scopeSlotCount + localIndex
|
val slot = scopeSlotCount + localIndex
|
||||||
localSlotCaptures[localIndex] = true
|
localSlotCaptures[localIndex] = true
|
||||||
forcedObjSlots.add(slot)
|
if (!slotTypes.containsKey(slot)) {
|
||||||
slotTypes[slot] = SlotType.OBJ
|
forcedObjSlots.add(slot)
|
||||||
|
slotTypes[slot] = SlotType.OBJ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9383,6 +9417,7 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
preloadExactCallableNames()
|
||||||
if (loopVarKeys.isNotEmpty()) {
|
if (loopVarKeys.isNotEmpty()) {
|
||||||
for (key in loopVarKeys) {
|
for (key in loopVarKeys) {
|
||||||
val localIndex = localSlotIndexByKey[key]
|
val localIndex = localSlotIndexByKey[key]
|
||||||
@ -9460,11 +9495,10 @@ class BytecodeCompiler(
|
|||||||
}
|
}
|
||||||
if (!scopeSlotNameMap.containsKey(key)) {
|
if (!scopeSlotNameMap.containsKey(key)) {
|
||||||
scopeSlotNameMap[key] = stmt.spec.name
|
scopeSlotNameMap[key] = stmt.spec.name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
preloadExactCallableNames()
|
|
||||||
}
|
|
||||||
is DelegatedVarDeclStatement -> {
|
is DelegatedVarDeclStatement -> {
|
||||||
val slotIndex = stmt.slotIndex
|
val slotIndex = stmt.slotIndex
|
||||||
val scopeId = stmt.scopeId ?: 0
|
val scopeId = stmt.scopeId ?: 0
|
||||||
|
|||||||
@ -38,17 +38,26 @@ class ImportManager(
|
|||||||
|
|
||||||
val packageNames: List<String> get() = imports.keys.toList()
|
val packageNames: List<String> get() = imports.keys.toList()
|
||||||
|
|
||||||
|
private class CacheCell(var scope: ModuleScope? = null)
|
||||||
|
|
||||||
private inner class Entry(
|
private inner class Entry(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val builder: suspend (ModuleScope) -> Unit,
|
val builder: suspend (ModuleScope) -> Unit,
|
||||||
var cachedScope: ModuleScope? = null
|
val cacheCell: CacheCell = CacheCell()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getScope(pos: Pos): ModuleScope {
|
suspend fun getScope(pos: Pos): ModuleScope {
|
||||||
cachedScope?.let { return it }
|
cacheCell.scope?.let { return it }
|
||||||
return ModuleScope(inner, pos, packageName).apply {
|
val module = ModuleScope(inner, pos, packageName)
|
||||||
cachedScope = this
|
cacheCell.scope = module
|
||||||
builder(this)
|
return try {
|
||||||
|
builder(module)
|
||||||
|
module
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (cacheCell.scope === module) {
|
||||||
|
cacheCell.scope = null
|
||||||
|
}
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,14 +161,14 @@ class ImportManager(
|
|||||||
op.withLock {
|
op.withLock {
|
||||||
ImportManager(rootScope, securityManager).apply {
|
ImportManager(rootScope, securityManager).apply {
|
||||||
for ((name, entry) in this@ImportManager.imports) {
|
for ((name, entry) in this@ImportManager.imports) {
|
||||||
imports[name] = Entry(entry.packageName, entry.builder, entry.cachedScope)
|
imports[name] = Entry(entry.packageName, entry.builder, entry.cacheCell)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invalidatePackageCache(name: String) {
|
fun invalidatePackageCache(name: String) {
|
||||||
op.withLock {
|
op.withLock {
|
||||||
imports[name]?.cachedScope = null
|
imports[name]?.cacheCell?.scope = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -247,6 +247,21 @@ class BytecodeRecentOpsTest {
|
|||||||
assertEquals(11, scope.eval("calc()").toInt())
|
assertEquals(11, scope.eval("calc()").toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun capturedLambdaCanCallListFillOnCapturedClassReceiver() = runTest {
|
||||||
|
val scope = Script.newScope()
|
||||||
|
val result = scope.eval(
|
||||||
|
"""
|
||||||
|
fun calc(n: Int) {
|
||||||
|
val xs = { List.fill(n) { it } }()
|
||||||
|
xs[4]
|
||||||
|
}
|
||||||
|
calc(5)
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
assertEquals(4, result.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun directLambdaLiteralCallWithCaptureUsesInlineBytecode() = runTest {
|
fun directLambdaLiteralCallWithCaptureUsesInlineBytecode() = runTest {
|
||||||
val scope = Script.newScope()
|
val scope = Script.newScope()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user