120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
package core
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.akyoto.dev/cli/q/src/asm"
|
|
"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/windows"
|
|
)
|
|
|
|
// 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)
|
|
|
|
if name == "syscall" {
|
|
return nil, f.CompileSyscall(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 := windows.X64InputRegisters[: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.Check(typ, fn.Parameters[i].Type) {
|
|
return nil, errors.New(&errors.TypeMismatch{
|
|
Encountered: typ.Name,
|
|
Expected: fn.Parameters[i].Type.Name,
|
|
ParameterName: fn.Parameters[i].Name,
|
|
}, f.File, parameters[i].Token.Position)
|
|
}
|
|
}
|
|
|
|
for _, register := range f.CPU.Output[:len(fn.ReturnTypes)] {
|
|
f.SaveRegister(register)
|
|
}
|
|
|
|
for _, register := range f.CPU.General {
|
|
if f.RegisterIsUsed(register) {
|
|
f.Register(asm.PUSH, register)
|
|
}
|
|
}
|
|
|
|
f.Call(fn.UniqueName)
|
|
|
|
for _, register := range registers {
|
|
if register == f.CPU.Output[0] && root.Parent != nil {
|
|
continue
|
|
}
|
|
|
|
f.FreeRegister(register)
|
|
}
|
|
|
|
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
|
register := f.CPU.General[i]
|
|
|
|
if f.RegisterIsUsed(register) {
|
|
f.Register(asm.POP, register)
|
|
}
|
|
}
|
|
|
|
return fn, nil
|
|
}
|