Files
llgo/cl/async.go
2024-08-04 10:22:22 +08:00

120 lines
3.2 KiB
Go

/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* 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 cl
import (
"go/types"
"strings"
llssa "github.com/goplus/llgo/ssa"
"golang.org/x/tools/go/ssa"
)
// TODO(lijie): need more generics, shouldn't limit to async.Promise
func promiseType(ty types.Type) (types.Type, bool) {
// ty is a generic type, so we need to check the package path and type name
if ptrTy, ok := ty.(*types.Pointer); ok {
ty = ptrTy.Elem()
if ty, ok := ty.(*types.Named); ok {
if ty.Obj().Pkg() == nil {
return nil, false
}
if ty.Obj().Pkg().Path() == "github.com/goplus/llgo/x/async" && ty.Obj().Name() == "Promise" {
return ty, true
}
}
}
return nil, false
}
// check function return async.Promise[T]
// TODO(lijie): make it generic
func isAsyncFunc(sig *types.Signature) bool {
r := sig.Results()
if r.Len() != 1 {
return false
}
ty := r.At(0).Type()
_, ok := promiseType(ty)
return ok
}
func (p *context) coAwait(b llssa.Builder, args []ssa.Value) llssa.Expr {
if !isAsyncFunc(b.Func.RawType().(*types.Signature)) {
panic("coAwait(promise *T) T: invalid context")
}
if len(args) == 1 {
// promise := p.compileValue(b, args[0])
b.Unreachable()
// return b.CoroutineAwait(promise)
}
panic("coAwait(promise *T) T: invalid arguments")
}
func (p *context) coSuspend(b llssa.Builder, final llssa.Expr) {
b.CoSuspend(b.AsyncToken(), final)
}
func (p *context) coDone(b llssa.Builder, args []ssa.Value) llssa.Expr {
if len(args) != 1 {
panic("coDone(promise *T): invalid arguments")
}
hdl := p.compileValue(b, args[0])
return b.CoDone(hdl)
}
func (p *context) coResume(b llssa.Builder, args []ssa.Value) {
if len(args) == 1 {
hdl := p.compileValue(b, args[0])
b.CoResume(hdl)
}
}
func (p *context) coReturn(b llssa.Builder, args []ssa.Value) {
cargs := make([]llssa.Expr, len(args))
for i, arg := range args {
cargs[i] = p.compileValue(b, arg)
}
b.CoReturn(cargs...)
}
func (p *context) coYield(b llssa.Builder, fn *ssa.Function, args []ssa.Value) {
typ := fn.Signature.Recv().Type()
mthds := p.goProg.MethodSets.MethodSet(typ)
// TODO(lijie): make llgo instruction callable (e.g. llgo.yield)
var setValue *ssa.Function
for i := 0; i < mthds.Len(); i++ {
m := mthds.At(i)
if ssaMthd := p.goProg.MethodValue(m); ssaMthd != nil {
if ssaMthd.Name() == "setValue" || strings.HasPrefix(ssaMthd.Name(), "setValue[") {
setValue = ssaMthd
break
}
}
}
if setValue == nil {
panic("coYield(): not found method setValue")
}
value := p.compileValue(b, args[1])
setValueFn, _, _ := p.funcOf(setValue)
b.CoYield(setValueFn, value)
}
func (p *context) coRun(b llssa.Builder, args []ssa.Value) {
panic("coRun(): not implemented")
}