package core import ( "fmt" "git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/expression" "git.akyoto.dev/cli/q/src/types" "git.akyoto.dev/cli/q/src/x64" ) // CompileCall executes a function call. // All call registers must hold the correct parameter values before the function invocation. // Registers that are in use must be saved if they are modified by the function. // After the function call, they must be restored in reverse order. func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { var ( pkg = f.Package nameNode = root.Children[0] fn *Function name string exists bool ) if nameNode.IsLeaf() { name = nameNode.Token.Text(f.File.Bytes) switch name { case "syscall": return nil, f.CompileSyscall(root) case "new": return &Function{ Output: []*Output{ { Type: &types.Pointer{ To: f.Types[root.Children[1].Token.Text(f.File.Bytes)], }, }, }, }, f.CompileNew(root) case "delete": return nil, f.CompileDelete(root) case "store": return nil, f.CompileMemoryStore(root) } } else { pkg = nameNode.Children[0].Token.Text(f.File.Bytes) name = nameNode.Children[1].Token.Text(f.File.Bytes) } if pkg == "kernel32" || pkg == "user32" || pkg == "gdi32" || pkg == "comctl32" { parameters := root.Children[1:] registers := x64.WindowsInputRegisters[:len(parameters)] for i := len(parameters) - 1; i >= 0; i-- { _, err := f.ExpressionToRegister(parameters[i], registers[i]) if err != nil { return nil, err } } f.DLLs = f.DLLs.Append(pkg, name) f.DLLCall(fmt.Sprintf("%s.%s", pkg, name)) return nil, nil } else if pkg != f.File.Package { if f.File.Imports == nil { return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position) } imp, exists := f.File.Imports[pkg] if !exists { return nil, errors.New(&errors.UnknownPackage{Name: pkg}, f.File, nameNode.Token.Position) } imp.Used = true } fn, exists = f.Functions[pkg+"."+name] if !exists { return nil, errors.New(&errors.UnknownFunction{Name: name}, f.File, nameNode.Token.Position) } parameters := root.Children[1:] registers := f.CPU.Input[:len(parameters)] for i := len(parameters) - 1; i >= 0; i-- { typ, err := f.ExpressionToRegister(parameters[i], registers[i]) if err != nil { return nil, err } if !types.Is(typ, fn.Input[i].Type) { return nil, errors.New(&errors.TypeMismatch{ Encountered: typ.Name(), Expected: fn.Input[i].Type.Name(), ParameterName: fn.Input[i].Name, }, f.File, parameters[i].Token.Position) } } f.CallSafe(fn, registers) return fn, nil }