mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Add fetch, exercise implementations for interfaces in speedy. (#10911)
* Draft: Daml Interfaces Speedy PoC Part of #10810 Extracted from #10670 changelog_begin changelog_end * Improve cacheing situation, add implements checks * scalafmt * Add comment for ImplementsDefRef * compile the new update expressions
This commit is contained in:
parent
d01f8e1c35
commit
61d214e451
@ -317,6 +317,7 @@ private[lf] final class Compiler(
|
||||
builder += compileKey(identifier, tmpl)
|
||||
builder += compileSignatories(identifier, tmpl)
|
||||
builder += compileObservers(identifier, tmpl)
|
||||
tmpl.implements.foreach(builder += compileImplements(identifier, _))
|
||||
|
||||
tmpl.choices.values.foreach(builder += compileChoice(identifier, tmpl, _))
|
||||
|
||||
@ -330,6 +331,14 @@ private[lf] final class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
module.interfaces.foreach { case (ifaceName, iface) =>
|
||||
val identifier = Identifier(pkgId, QualifiedName(module.name, ifaceName))
|
||||
builder += compileFetchInterface(identifier)
|
||||
iface.choices.values.foreach(
|
||||
builder += compileChoiceInterface(identifier, _)
|
||||
)
|
||||
}
|
||||
|
||||
builder.result()
|
||||
}
|
||||
|
||||
@ -773,9 +782,8 @@ private[lf] final class Compiler(
|
||||
compileBlock(bindings, body)
|
||||
case UpdateFetch(tmplId, coidE) =>
|
||||
FetchDefRef(tmplId)(compile(coidE))
|
||||
case UpdateFetchInterface(_, _) =>
|
||||
// TODO https://github.com/digital-asset/daml/issues/10810
|
||||
sys.error("Interfaces not supported")
|
||||
case UpdateFetchInterface(ifaceId, coidE) =>
|
||||
FetchDefRef(ifaceId)(compile(coidE))
|
||||
case UpdateEmbedExpr(_, e) =>
|
||||
compileEmbedExpr(e)
|
||||
case UpdateCreate(tmplId, arg) =>
|
||||
@ -787,9 +795,8 @@ private[lf] final class Compiler(
|
||||
choiceId = chId,
|
||||
argument = compile(argE),
|
||||
)
|
||||
case UpdateExerciseInterface(_, _, _, _) =>
|
||||
// TODO https://github.com/digital-asset/daml/issues/10810
|
||||
sys.error("Interfaces not supported")
|
||||
case UpdateExerciseInterface(ifaceId, chId, cidE, argE) =>
|
||||
ChoiceDefRef(ifaceId, chId)(compile(cidE), compile(argE))
|
||||
case UpdateExerciseByKey(tmplId, chId, keyE, argE) =>
|
||||
compileExerciseByKey(tmplId, compile(keyE), chId, compile(argE))
|
||||
case UpdateGetTime =>
|
||||
@ -1006,6 +1013,24 @@ private[lf] final class Compiler(
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def compileChoiceInterface(
|
||||
ifaceId: TypeConName,
|
||||
choice: InterfaceChoice,
|
||||
): (SDefinitionRef, SDefinition) =
|
||||
topLevelFunction(ChoiceDefRef(ifaceId, choice.name), 2) { case List(cidPos, choiceArgPos, _) =>
|
||||
withEnv { _ =>
|
||||
let(
|
||||
SBUPreFetchInterface(ifaceId)(svar(cidPos))
|
||||
) { tmplArgPos =>
|
||||
SBUChoiceInterface(ifaceId, choice.name)(
|
||||
svar(cidPos),
|
||||
svar(choiceArgPos),
|
||||
svar(tmplArgPos),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def compileChoice(
|
||||
tmplId: TypeConName,
|
||||
tmpl: Template,
|
||||
@ -1397,6 +1422,22 @@ private[lf] final class Compiler(
|
||||
compileFetchBody(tmplId, tmpl)(cidPos, None, tokenPos)
|
||||
}
|
||||
|
||||
private[this] def compileFetchInterface(
|
||||
ifaceId: Identifier
|
||||
): (SDefinitionRef, SDefinition) =
|
||||
topLevelFunction(FetchDefRef(ifaceId), 2) { case List(cidPos, _) =>
|
||||
withEnv { _ =>
|
||||
let(
|
||||
SBUPreFetchInterface(ifaceId)(svar(cidPos))
|
||||
) { tmplArgPos =>
|
||||
SBUFetchInterface(ifaceId)(
|
||||
svar(cidPos),
|
||||
svar(tmplArgPos),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private[this] def compileKey(
|
||||
tmplId: Identifier,
|
||||
tmpl: Template,
|
||||
@ -1424,6 +1465,18 @@ private[lf] final class Compiler(
|
||||
compile(tmpl.observers)
|
||||
}
|
||||
|
||||
// Turn a template value into an interface value. Since interfaces have a
|
||||
// toll-free representation (for now), this is just the identity function.
|
||||
// But the existence of ImplementsDefRef implies that the template implements
|
||||
// the interface, which is useful in itself.
|
||||
private[this] def compileImplements(
|
||||
tmplId: Identifier,
|
||||
ifaceId: Identifier,
|
||||
): (SDefinitionRef, SDefinition) =
|
||||
topLevelFunction(ImplementsDefRef(tmplId, ifaceId), 1) { case List(tmplPos) =>
|
||||
svar(tmplPos)
|
||||
}
|
||||
|
||||
private[this] def compileCreate(
|
||||
tmplId: Identifier,
|
||||
tmpl: Template,
|
||||
|
@ -238,6 +238,7 @@ object Profile {
|
||||
implicit val keyDefRef: Allowed[KeyDefRef] = allowAll
|
||||
implicit val signatoriesDefRef: Allowed[SignatoriesDefRef] = allowAll
|
||||
implicit val observersDefRef: Allowed[ObserversDefRef] = allowAll
|
||||
implicit val implementsDefRef: Allowed[ImplementsDefRef] = allowAll
|
||||
implicit val choiceDefRef: Allowed[ChoiceDefRef] = allowAll
|
||||
implicit val fetchDefRef: Allowed[FetchDefRef] = allowAll
|
||||
implicit val choiceByKeyDefRef: Allowed[ChoiceByKeyDefRef] = allowAll
|
||||
@ -260,6 +261,8 @@ object Profile {
|
||||
case KeyDefRef(tmplRef) => s"keyAndMaintainers @${tmplRef.qualifiedName}"
|
||||
case SignatoriesDefRef(tmplRef) => s"signatories @${tmplRef.qualifiedName}"
|
||||
case ObserversDefRef(tmplRef) => s"observers @${tmplRef.qualifiedName}"
|
||||
case ImplementsDefRef(tmplRef, ifaceId) =>
|
||||
s"implements @${tmplRef.qualifiedName} @${ifaceId.qualifiedName}"
|
||||
case ChoiceDefRef(tmplRef, name) => s"exercise @${tmplRef.qualifiedName} ${name}"
|
||||
case FetchDefRef(tmplRef) => s"fetch @${tmplRef.qualifiedName}"
|
||||
case ChoiceByKeyDefRef(tmplRef, name) =>
|
||||
|
@ -1072,6 +1072,115 @@ private[lf] object SBuiltin {
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to SBUFetch but doesn't perform any checks on the template id, and is never "by key".
|
||||
final case class SBUPreFetchInterface(ifaceId: TypeConName) extends OnLedgerBuiltin(1) {
|
||||
override protected def execute(
|
||||
args: util.ArrayList[SValue],
|
||||
machine: Machine,
|
||||
onLedger: OnLedger,
|
||||
): Unit = {
|
||||
val coid = getSContractId(args, 0)
|
||||
onLedger.cachedContracts.get(coid) match {
|
||||
case Some(cached) => {
|
||||
machine.returnValue = cached.value
|
||||
}
|
||||
case None => {
|
||||
throw SpeedyHungry(
|
||||
SResultNeedContract(
|
||||
coid,
|
||||
ifaceId, // not actually used, maybe this param should be dropped from SResultNeedContract
|
||||
onLedger.committers,
|
||||
{ case V.ContractInst(actualTmplId, V.VersionedValue(_, arg), _) =>
|
||||
val keyExpr = SEApp(SEVal(KeyDefRef(actualTmplId)), Array(SELocS(1)))
|
||||
machine.pushKont(KCacheContract(machine, actualTmplId, coid))
|
||||
machine.ctrl = SELet1(
|
||||
SEImportValue(Ast.TTyCon(actualTmplId), arg),
|
||||
cachedContractStruct(
|
||||
SELocS(1),
|
||||
SEApp(SEVal(SignatoriesDefRef(actualTmplId)), Array(SELocS(1))),
|
||||
SEApp(SEVal(ObserversDefRef(actualTmplId)), Array(SELocS(1))),
|
||||
keyExpr,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SBUFetchInterface uses the contract payload obtained from SBUPreFetchInterface
|
||||
// to call the proper FetchDefRef with the actual template id, and performs an
|
||||
// implements check.
|
||||
//
|
||||
// Interfaces have a "toll-free" representation with the underlying template,
|
||||
// since the template's SRecord already includes the type constructor (templateId)
|
||||
// of its template, we shouldn't need to wrap or change the fetched template value
|
||||
// in any way. (Unless we later need to enforce the distinction between templates
|
||||
// and interfaces at the speedy value level.)
|
||||
final case class SBUFetchInterface(
|
||||
ifaceId: TypeConName
|
||||
) extends SBuiltin(2) {
|
||||
override private[speedy] def execute(
|
||||
args: util.ArrayList[SValue],
|
||||
machine: Machine,
|
||||
): Unit = {
|
||||
val coid = getSContractId(args, 0)
|
||||
val SRecord(tmplId, _, _) = getSRecord(args, 1)
|
||||
// After SBUPreFetchInterface, the template's package should already be loaded
|
||||
// in compiledPackages, so SImplementsDefRef will be defined if the template
|
||||
// implements the interface.
|
||||
machine.compiledPackages.getDefinition(ImplementsDefRef(tmplId, ifaceId)) match {
|
||||
case Some(_) =>
|
||||
machine.ctrl = FetchDefRef(tmplId)(
|
||||
SEValue(SContractId(coid)),
|
||||
SEValue(SToken),
|
||||
)
|
||||
case None =>
|
||||
machine.ctrl = SEDamlException(
|
||||
IE.WronglyTypedContract(coid, ifaceId, tmplId)
|
||||
// TODO https://github.com/digital-asset/daml/issues/10810:
|
||||
// Maybe create a more specific exception.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Very similar to SBUFetchInterface. We use the payload from SBUPreFetchInterface
|
||||
// to call the appropriate ChoiceDefRef with the actual template id, and performs
|
||||
// an implements check.
|
||||
final case class SBUChoiceInterface(
|
||||
ifaceId: TypeConName,
|
||||
choiceName: ChoiceName,
|
||||
) extends SBuiltin(2) {
|
||||
override private[speedy] def execute(
|
||||
args: util.ArrayList[SValue],
|
||||
machine: Machine,
|
||||
): Unit = {
|
||||
val coid = getSContractId(args, 0)
|
||||
val choiceArg = args.get(1)
|
||||
val SRecord(tmplId, _, _) = getSRecord(args, 2)
|
||||
// After SBUPreFetchInterface, the template's package should already be loaded
|
||||
// in compiledPackages, so SImplementsDefRef will be defined if the template
|
||||
// implements the interface.
|
||||
machine.compiledPackages.getDefinition(ImplementsDefRef(tmplId, ifaceId)) match {
|
||||
case Some(_) =>
|
||||
machine.ctrl = ChoiceDefRef(tmplId, choiceName)(
|
||||
SEValue(SContractId(coid)),
|
||||
SEValue(choiceArg),
|
||||
SEValue(SToken),
|
||||
)
|
||||
case None =>
|
||||
machine.ctrl = SEDamlException(
|
||||
IE.WronglyTypedContract(coid, ifaceId, tmplId)
|
||||
// TODO https://github.com/digital-asset/daml/issues/10810:
|
||||
// Maybe create a more specific exception.
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** $insertFetch[tid]
|
||||
* :: ContractId a
|
||||
* -> List Party (signatories)
|
||||
|
@ -466,6 +466,12 @@ object SExpr {
|
||||
final case class SignatoriesDefRef(ref: DefinitionRef) extends SDefinitionRef
|
||||
final case class ObserversDefRef(ref: DefinitionRef) extends SDefinitionRef
|
||||
|
||||
/** ImplementsDefRef(ref=templateId, ifaceId) points to a function that converts a
|
||||
* template value to an interface value. (This is currently an identity function.)
|
||||
* The existence of this definition signals that the template implements the interface.
|
||||
*/
|
||||
final case class ImplementsDefRef(ref: DefinitionRef, ifaceId: TypeConName) extends SDefinitionRef
|
||||
|
||||
//
|
||||
// List builtins (equalList) are implemented as recursive
|
||||
// definition to save java stack
|
||||
|
Loading…
Reference in New Issue
Block a user