2022-11-25 01:58:10 +00:00
|
|
|
package ph7
|
|
|
|
|
|
|
|
/*
|
2022-11-25 07:56:56 +00:00
|
|
|
#include "cgo.h"
|
2022-11-25 01:58:10 +00:00
|
|
|
#include "ph7.h"
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-11-25 05:19:40 +00:00
|
|
|
"io"
|
2022-11-25 07:56:56 +00:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
pointer "github.com/mattn/go-pointer"
|
2022-11-25 01:58:10 +00:00
|
|
|
)
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
type ResultCode int
|
|
|
|
|
|
|
|
const (
|
|
|
|
ResultCodeOK = ResultCode(C.PH7_OK)
|
|
|
|
ResultCodeNoMem = ResultCode(C.PH7_NOMEM)
|
|
|
|
ResultCodeAbort = ResultCode(C.PH7_ABORT)
|
|
|
|
ResultCodeIOErr = ResultCode(C.PH7_IO_ERR)
|
|
|
|
ResultCodeLooked = ResultCode(C.PH7_LOOKED)
|
|
|
|
ResultCodeCorrupt = ResultCode(C.PH7_CORRUPT)
|
|
|
|
ResultCodeCompileErr = ResultCode(C.PH7_COMPILE_ERR)
|
|
|
|
ResultCodeVMErr = ResultCode(C.PH7_VM_ERR)
|
|
|
|
)
|
|
|
|
|
|
|
|
func (r ResultCode) String() string {
|
|
|
|
switch r {
|
|
|
|
case ResultCodeOK:
|
|
|
|
return "PH7_OK"
|
|
|
|
case ResultCodeNoMem:
|
|
|
|
return "PH7_NOMEM"
|
|
|
|
case ResultCodeAbort:
|
|
|
|
return "PH7_ABORT"
|
|
|
|
case ResultCodeIOErr:
|
|
|
|
return "PH7_IO_ERR"
|
|
|
|
case ResultCodeLooked:
|
|
|
|
return "PH7_LOOKED"
|
|
|
|
case ResultCodeCorrupt:
|
|
|
|
return "PH7_CORRUPT"
|
|
|
|
case ResultCodeCompileErr:
|
|
|
|
return "PH7_COMPILE_ERR"
|
|
|
|
case ResultCodeVMErr:
|
|
|
|
return "PH7_VM_ERR"
|
|
|
|
default:
|
|
|
|
return "UNKNOWN"
|
|
|
|
}
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
type Error struct {
|
|
|
|
Code ResultCode
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
func newError(code C.int) error {
|
2022-11-27 16:53:49 +00:00
|
|
|
if code == C.PH7_OK {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return Error{
|
|
|
|
Code: ResultCode(code),
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
2022-11-25 04:51:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e Error) Error() string {
|
2022-11-27 16:53:49 +00:00
|
|
|
return fmt.Sprintf("ph7: %s", e.Code.String())
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 16:53:49 +00:00
|
|
|
type CompileError struct {
|
|
|
|
Msg string
|
|
|
|
}
|
2022-11-27 15:30:53 +00:00
|
|
|
|
2022-11-27 16:53:49 +00:00
|
|
|
func (e CompileError) Error() string {
|
|
|
|
return fmt.Sprintf("ph7 compile error: %s", e.Msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
type Engine C.ph7
|
2022-11-25 01:58:10 +00:00
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
func NewEngine() (*Engine, error) {
|
|
|
|
var cengine *C.ph7
|
|
|
|
result := C.ph7_init(&cengine)
|
2022-11-25 01:58:10 +00:00
|
|
|
if result != C.PH7_OK {
|
2022-11-27 15:30:53 +00:00
|
|
|
return nil, newError(result)
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
return (*Engine)(cengine), nil
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
func (e *Engine) ErrLog() (string, error) {
|
2022-11-25 01:58:10 +00:00
|
|
|
var s *C.char
|
|
|
|
var n C.int
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
err := newError(C.engine_config_err_log((*C.ph7)(e), &s, &n))
|
2022-11-25 04:51:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return C.GoStringN(s, n), nil
|
|
|
|
}
|
|
|
|
|
2022-11-27 18:31:44 +00:00
|
|
|
/*
|
|
|
|
We don't bother ph7_config verbs other than PH7_CONFIG_ERR_LOG.
|
|
|
|
PH7_CONFIG_ERR_OUTPUT is another way of getting compile error messages,
|
|
|
|
which we don't need separately from returning CompileError values.
|
|
|
|
PH7_CONFIG_ERR_ABORT doesn't do anything.
|
|
|
|
*/
|
2022-11-27 16:32:17 +00:00
|
|
|
|
2022-11-27 16:53:49 +00:00
|
|
|
func (e *Engine) compileError(code C.int) error {
|
|
|
|
switch code {
|
|
|
|
case C.PH7_OK:
|
|
|
|
return nil
|
|
|
|
case C.PH7_COMPILE_ERR:
|
|
|
|
errLog, errLogErr := e.ErrLog()
|
|
|
|
if errLogErr != nil || errLog == "" {
|
|
|
|
return newError(code)
|
|
|
|
}
|
|
|
|
return CompileError{
|
|
|
|
Msg: errLog,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return newError(code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
func (e *Engine) Compile(source []byte, phpOnly bool) (*VM, error) {
|
2022-11-25 01:58:10 +00:00
|
|
|
csource := C.CBytes(source)
|
2022-11-25 05:19:40 +00:00
|
|
|
defer C.free(csource)
|
2022-11-25 01:58:10 +00:00
|
|
|
var flags C.int
|
|
|
|
if phpOnly {
|
|
|
|
flags |= C.PH7_PHP_ONLY
|
|
|
|
}
|
2022-11-27 15:30:53 +00:00
|
|
|
var cvm *C.ph7_vm
|
2022-11-27 16:53:49 +00:00
|
|
|
err := e.compileError(C.ph7_compile_v2((*C.ph7)(e), (*C.char)(csource), C.int(len(source)), &cvm, flags))
|
2022-11-25 04:51:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
return newVM(cvm), nil
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 18:31:44 +00:00
|
|
|
func (e *Engine) CompileFile(path string, phpOnly bool) (*VM, error) {
|
|
|
|
cpath := C.CString(path)
|
|
|
|
defer C.free(unsafe.Pointer(cpath))
|
|
|
|
|
|
|
|
var flags C.int
|
|
|
|
if phpOnly {
|
|
|
|
flags |= C.PH7_PHP_ONLY
|
|
|
|
}
|
|
|
|
var cvm *C.ph7_vm
|
|
|
|
err := e.compileError(C.ph7_compile_file((*C.ph7)(e), cpath, &cvm, flags))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return newVM(cvm), nil
|
|
|
|
}
|
|
|
|
|
2022-11-27 15:30:53 +00:00
|
|
|
func (e *Engine) Close() error {
|
|
|
|
return newError(C.ph7_release((*C.ph7)(e)))
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
type Context C.ph7_context
|
|
|
|
|
|
|
|
func (c *Context) ResultInt(i int) error {
|
|
|
|
return newError(C.ph7_result_int((*C.ph7_context)(c), C.int(i)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultInt64(i int64) error {
|
|
|
|
return newError(C.ph7_result_int64((*C.ph7_context)(c), C.ph7_int64(i)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultBool(b bool) error {
|
|
|
|
var cb C.int
|
|
|
|
if b {
|
|
|
|
cb = 1
|
|
|
|
}
|
|
|
|
return newError(C.ph7_result_bool((*C.ph7_context)(c), cb))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultDouble(d float64) error {
|
|
|
|
return newError(C.ph7_result_double((*C.ph7_context)(c), C.double(d)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultNull() error {
|
|
|
|
return newError(C.ph7_result_null((*C.ph7_context)(c)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultString(s string) error {
|
|
|
|
cs := C.CString(s)
|
|
|
|
defer C.free(unsafe.Pointer(cs))
|
|
|
|
|
|
|
|
return newError(C.ph7_result_string((*C.ph7_context)(c), cs, C.int(len(s))))
|
|
|
|
}
|
|
|
|
|
2022-11-28 04:49:36 +00:00
|
|
|
type resultWriter Context
|
|
|
|
|
|
|
|
func (rw *resultWriter) Write(p []byte) (int, error) {
|
|
|
|
cp := C.CBytes(p)
|
|
|
|
defer C.free(cp)
|
|
|
|
err := newError(C.ph7_result_string(
|
|
|
|
(*C.ph7_context)(rw),
|
|
|
|
(*C.char)(cp),
|
|
|
|
C.int(len(p))))
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Context) ResultWriter() io.Writer {
|
|
|
|
return (*resultWriter)(c)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ph7_result_string_format is redundant when we have fmt.Sprintf and fmt.Fprintf.
|
2022-11-27 16:32:17 +00:00
|
|
|
|
|
|
|
func (c *Context) ResultValue(value *Value) error {
|
|
|
|
return newError(C.ph7_result_value((*C.ph7_context)(c), (*C.ph7_value)(value)))
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO (*Context).ResultResource
|
|
|
|
|
2022-11-28 04:49:36 +00:00
|
|
|
type outputWriter Context
|
|
|
|
|
|
|
|
func (ow *outputWriter) Write(p []byte) (int, error) {
|
2022-11-27 22:45:18 +00:00
|
|
|
cp := C.CBytes(p)
|
|
|
|
defer C.free(cp)
|
2022-11-28 04:49:36 +00:00
|
|
|
err := newError(C.ph7_context_output(
|
|
|
|
(*C.ph7_context)(ow),
|
|
|
|
(*C.char)(cp),
|
|
|
|
C.int(len(p))))
|
2022-11-27 22:45:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return len(p), nil
|
2022-11-27 16:32:17 +00:00
|
|
|
}
|
|
|
|
|
2022-11-28 04:49:36 +00:00
|
|
|
func (c *Context) OutputWriter() io.Writer {
|
|
|
|
return (*outputWriter)(c)
|
|
|
|
}
|
|
|
|
|
2022-11-28 00:10:28 +00:00
|
|
|
type Severity int
|
|
|
|
|
|
|
|
const (
|
|
|
|
SeverityErr Severity = C.PH7_CTX_ERR
|
|
|
|
SeverityWarning Severity = C.PH7_CTX_WARNING
|
|
|
|
SeverityNotice Severity = C.PH7_CTX_NOTICE
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Context) ThrowError(severity Severity, msg string) error {
|
|
|
|
cmsg := C.CString(msg)
|
|
|
|
defer C.free(unsafe.Pointer(cmsg))
|
|
|
|
|
|
|
|
return newError(C.ph7_context_throw_error((*C.ph7_context)(c), C.int(severity), cmsg))
|
|
|
|
}
|
|
|
|
|
2022-11-28 04:49:36 +00:00
|
|
|
// ph7_context_throw_error_format is redundant when we have fmt.Sprintf.
|
2022-11-27 16:32:17 +00:00
|
|
|
|
|
|
|
func (c *Context) RandomNum() uint {
|
|
|
|
return uint(C.ph7_context_random_num((*C.ph7_context)(c)))
|
|
|
|
}
|
|
|
|
|
2022-11-27 21:51:01 +00:00
|
|
|
func (c *Context) RandomString(length int) (string, error) {
|
|
|
|
buf := (*C.char)(C.malloc(C.size_t(length)))
|
|
|
|
defer C.free(unsafe.Pointer(buf))
|
|
|
|
|
|
|
|
err := newError(C.ph7_context_random_string((*C.ph7_context)(c), nil, C.int(length)))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return C.GoStringN(buf, C.int(length)), nil
|
|
|
|
}
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO (*Context).UserData
|
|
|
|
// TODO (*Context).PushAuxData
|
|
|
|
// TODO (*Context).PeekAuxData
|
|
|
|
// TODO (*Context).PopAuxData
|
2022-11-28 04:49:36 +00:00
|
|
|
|
|
|
|
func (c *Context) ResultBufLength() uint {
|
|
|
|
return uint(C.ph7_context_result_buf_length((*C.ph7_context)(c)))
|
|
|
|
}
|
2022-11-28 01:35:51 +00:00
|
|
|
|
|
|
|
func (c *Context) FunctionName() string {
|
|
|
|
return C.GoString(C.ph7_function_name((*C.ph7_context)(c)))
|
|
|
|
}
|
2022-11-27 16:32:17 +00:00
|
|
|
|
|
|
|
// TODO (*Context).AllocChunk
|
|
|
|
// TODO (*Context).ReallocChunk
|
|
|
|
// TODO (*Context).FreeChunk
|
|
|
|
|
2022-11-27 04:45:16 +00:00
|
|
|
type Value C.ph7_value
|
|
|
|
|
2022-11-28 04:53:51 +00:00
|
|
|
// TODO Make *Value a fmt.Formatter.
|
2022-11-28 04:49:36 +00:00
|
|
|
|
2022-11-27 21:51:01 +00:00
|
|
|
func (v *Value) ToInt() int {
|
|
|
|
return int(C.ph7_value_to_int((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) ToBool() bool {
|
|
|
|
return C.ph7_value_to_bool((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) ToInt64() int64 {
|
|
|
|
return int64(C.ph7_value_to_int64((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) ToDouble() float64 {
|
|
|
|
return float64(C.ph7_value_to_double((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) ToString() string {
|
|
|
|
var strLen C.int
|
|
|
|
cstr := C.ph7_value_to_string((*C.ph7_value)(v), &strLen)
|
|
|
|
return C.GoStringN(cstr, strLen)
|
|
|
|
}
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO (*Value).ToResource
|
2022-11-27 21:51:01 +00:00
|
|
|
|
|
|
|
func (v *Value) Compare(v2 *Value) int {
|
|
|
|
return int(C.ph7_value_compare((*C.ph7_value)(v), (*C.ph7_value)(v2), 0))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) CompareStrict(v2 *Value) int {
|
|
|
|
return int(C.ph7_value_compare((*C.ph7_value)(v), (*C.ph7_value)(v2), 1))
|
|
|
|
}
|
2022-11-27 16:32:17 +00:00
|
|
|
|
2022-11-28 04:49:36 +00:00
|
|
|
func (v *Value) Int(n int) error {
|
|
|
|
return newError(C.ph7_value_int((*C.ph7_value)(v), C.int(n)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) Int64(n int64) error {
|
|
|
|
return newError(C.ph7_value_int64((*C.ph7_value)(v), C.ph7_int64(n)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) Bool(b bool) error {
|
|
|
|
var cb C.int
|
|
|
|
if b {
|
|
|
|
cb = 1
|
|
|
|
}
|
|
|
|
return newError(C.ph7_value_bool((*C.ph7_value)(v), cb))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) Null() error {
|
|
|
|
return newError(C.ph7_value_null((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) Double(d float64) error {
|
|
|
|
return newError(C.ph7_value_double((*C.ph7_value)(v), C.double(d)))
|
|
|
|
}
|
|
|
|
|
2022-11-28 04:53:51 +00:00
|
|
|
// TODO Rename or remove this so I can make *Value a Stringer.
|
2022-11-28 04:49:36 +00:00
|
|
|
func (v *Value) String(s string) error {
|
|
|
|
cstr := C.CString(s)
|
|
|
|
defer C.free(unsafe.Pointer(cstr))
|
|
|
|
return newError(C.ph7_value_string(
|
|
|
|
(*C.ph7_value)(v),
|
|
|
|
cstr,
|
|
|
|
C.int(len(s))))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) Write(p []byte) error {
|
|
|
|
cp := C.CBytes(p)
|
|
|
|
defer C.free(cp)
|
|
|
|
return newError(C.ph7_value_string(
|
|
|
|
(*C.ph7_value)(v),
|
|
|
|
(*C.char)(cp),
|
|
|
|
C.int(len(p))))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) ResetStringCursor() error {
|
|
|
|
return newError(C.ph7_value_reset_string_cursor((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO (*Value).Resource
|
2022-11-28 04:49:36 +00:00
|
|
|
|
|
|
|
func (v *Value) Release() error {
|
|
|
|
return newError(C.ph7_value_release((*C.ph7_value)(v)))
|
|
|
|
}
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO (*Value).ArrayFetch
|
|
|
|
// TODO (*Value).ArrayWalk
|
|
|
|
// TODO (*Value).ArrayAddElem
|
|
|
|
// TODO (*Value).ArrayAddStrkeyElem
|
|
|
|
// TODO (*Value).ArrayAddIntkeyElem
|
|
|
|
// TODO (*Value).ArrayCount
|
|
|
|
// TODO (*Value).ObjectWalk
|
|
|
|
// TODO (*Value).ObjectFetchAttr
|
|
|
|
// TODO (*Value).ObjectGetClassName
|
2022-11-27 22:14:52 +00:00
|
|
|
|
|
|
|
func (v *Value) IsInt() bool {
|
|
|
|
return C.ph7_value_is_int((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsFloat() bool {
|
|
|
|
return C.ph7_value_is_float((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsBool() bool {
|
|
|
|
return C.ph7_value_is_bool((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsString() bool {
|
|
|
|
return C.ph7_value_is_string((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsNull() bool {
|
|
|
|
return C.ph7_value_is_null((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsNumeric() bool {
|
|
|
|
return C.ph7_value_is_numeric((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsCallable() bool {
|
|
|
|
return C.ph7_value_is_callable((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsScalar() bool {
|
|
|
|
return C.ph7_value_is_scalar((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsArray() bool {
|
|
|
|
return C.ph7_value_is_array((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsObject() bool {
|
|
|
|
return C.ph7_value_is_object((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsResource() bool {
|
|
|
|
return C.ph7_value_is_resource((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Value) IsEmpty() bool {
|
|
|
|
return C.ph7_value_is_empty((*C.ph7_value)(v)) != 0
|
|
|
|
}
|
2022-11-27 16:32:17 +00:00
|
|
|
|
2022-11-27 19:02:24 +00:00
|
|
|
// ph7_lib_init is unnecessary when single-threaded, and possibly always.
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO Config
|
2022-11-28 04:49:36 +00:00
|
|
|
|
|
|
|
func Shutdown() error {
|
|
|
|
return newError(C.ph7_lib_shutdown())
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsThreadsafe() bool {
|
|
|
|
return C.ph7_lib_is_threadsafe() != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func Version() string {
|
|
|
|
return C.GoString(C.ph7_lib_version())
|
|
|
|
}
|
|
|
|
|
|
|
|
func Signature() string {
|
|
|
|
return C.GoString(C.ph7_lib_signature())
|
|
|
|
}
|
|
|
|
|
|
|
|
func Ident() string {
|
|
|
|
return C.GoString(C.ph7_lib_ident())
|
|
|
|
}
|
|
|
|
|
|
|
|
func Copyright() string {
|
|
|
|
return C.GoString(C.ph7_lib_copyright()) +
|
|
|
|
"\ngo-ph7 by Brandon Dyck. Do whatever the hell you want with it."
|
|
|
|
}
|
2022-11-27 04:45:16 +00:00
|
|
|
|
2022-11-25 01:58:10 +00:00
|
|
|
type VM struct {
|
2022-11-28 04:49:36 +00:00
|
|
|
ptr *C.ph7_vm
|
|
|
|
pointerKey unsafe.Pointer
|
|
|
|
outputWriter io.Writer
|
2022-11-27 04:45:16 +00:00
|
|
|
outputWriteErr error
|
|
|
|
userFunctionKeys []unsafe.Pointer
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-25 07:56:56 +00:00
|
|
|
func newVM(cvm *C.ph7_vm) *VM {
|
|
|
|
vm := new(VM)
|
|
|
|
vm.ptr = cvm
|
|
|
|
vm.pointerKey = pointer.Save(vm)
|
|
|
|
return vm
|
|
|
|
}
|
|
|
|
|
|
|
|
//export write_vm_output
|
|
|
|
func write_vm_output(buf unsafe.Pointer, bufLen C.uint, userData unsafe.Pointer) C.int {
|
|
|
|
vm := pointer.Restore(userData).(*VM)
|
|
|
|
gobuf := C.GoBytes(buf, C.int(bufLen))
|
|
|
|
_, vm.outputWriteErr = vm.outputWriter.Write(gobuf)
|
|
|
|
if vm.outputWriteErr != nil {
|
|
|
|
return C.int(ResultCodeAbort)
|
|
|
|
}
|
|
|
|
return C.int(ResultCodeOK)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vm *VM) SetOutputWriter(w io.Writer) error {
|
|
|
|
vm.outputWriter = w
|
|
|
|
return newError(C.vm_set_output_callback(vm.ptr, vm.pointerKey))
|
|
|
|
}
|
|
|
|
|
2022-11-28 00:10:28 +00:00
|
|
|
// TODO PH7_VM_CONFIG_IMPORT_PATH
|
|
|
|
// TODO PH7_VM_CONFIG_RECURSION_DEPTH
|
2022-11-28 04:49:36 +00:00
|
|
|
|
|
|
|
func (vm *VM) OutputLength() (uint, error) {
|
|
|
|
var n C.uint
|
|
|
|
err := newError(C.vm_output_length(vm.ptr, &n))
|
|
|
|
return uint(n), err
|
|
|
|
}
|
|
|
|
|
2022-11-28 00:10:28 +00:00
|
|
|
// TODO PH7_VM_CONFIG_CREATE_SUPER
|
|
|
|
// TODO PH7_VM_CONFIG_CREATE_VAR
|
|
|
|
// TODO PH7_VM_CONFIG_HTTP_REQUEST
|
|
|
|
// TODO PH7_VM_CONFIG_SERVER_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_ENV_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_SESSION_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_POST_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_GET_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_COOKIE_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_HEADER_ATTR
|
|
|
|
// TODO PH7_VM_CONFIG_EXEC_VALUE
|
|
|
|
// TODO PH7_VM_CONFIG_IO_STREAM
|
|
|
|
// TODO PH7_VM_CONFIG_ARGV_ENTRY
|
|
|
|
// TODO PH7_VM_CONFIG_ERR_LOG_HANDLER
|
|
|
|
|
2022-11-27 16:32:17 +00:00
|
|
|
// TODO (*VM).Dump(w io.Writer)
|
|
|
|
|
2022-11-25 07:56:56 +00:00
|
|
|
func (vm *VM) OutputWriteError() error {
|
|
|
|
return vm.outputWriteErr
|
2022-11-25 05:19:40 +00:00
|
|
|
}
|
|
|
|
|
2022-11-28 00:10:28 +00:00
|
|
|
func (vm *VM) ReportErrors() error {
|
|
|
|
return newError(C.vm_config_err_report((*C.ph7_vm)(vm.ptr)))
|
|
|
|
}
|
|
|
|
|
2022-11-25 07:56:56 +00:00
|
|
|
func (vm *VM) ExtractOutput() (string, error) {
|
2022-11-25 01:58:10 +00:00
|
|
|
var s *C.char
|
|
|
|
var n C.int
|
|
|
|
|
2022-11-25 04:51:44 +00:00
|
|
|
err := newError(C.vm_extract_output(vm.ptr, &s, &n))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return C.GoStringN(s, n), nil
|
|
|
|
}
|
|
|
|
|
2022-11-25 07:56:56 +00:00
|
|
|
func (vm *VM) Exec() error {
|
2022-11-26 23:30:16 +00:00
|
|
|
// TODO return exit status
|
2022-11-25 07:56:56 +00:00
|
|
|
return newError(C.ph7_vm_exec(vm.ptr, (*C.int)(nil)))
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
|
|
|
|
2022-11-26 23:30:16 +00:00
|
|
|
func (vm *VM) Reset() error {
|
2022-11-28 04:49:36 +00:00
|
|
|
vm.outputWriteErr = nil
|
2022-11-26 23:30:16 +00:00
|
|
|
return newError(C.ph7_vm_reset(vm.ptr))
|
|
|
|
}
|
|
|
|
|
2022-11-25 07:56:56 +00:00
|
|
|
func (vm *VM) Close() error {
|
|
|
|
pointer.Unref(vm.pointerKey)
|
2022-11-27 04:45:16 +00:00
|
|
|
for _, key := range vm.userFunctionKeys {
|
|
|
|
pointer.Unref(key)
|
|
|
|
}
|
2022-11-25 07:56:56 +00:00
|
|
|
return newError(C.ph7_vm_release(vm.ptr))
|
2022-11-25 01:58:10 +00:00
|
|
|
}
|
2022-11-27 04:45:16 +00:00
|
|
|
|
|
|
|
type ForeignFunction interface {
|
|
|
|
Call(ctx *Context, args []*Value) ResultCode
|
|
|
|
}
|
|
|
|
|
|
|
|
//export call_foreign_function
|
|
|
|
func call_foreign_function(ctx *C.ph7_context, argc C.int, argv **C.ph7_value) C.int {
|
|
|
|
fkey := C.ph7_context_user_data(ctx)
|
2022-11-27 19:02:24 +00:00
|
|
|
// TODO it looks like ph7 looks for a VFS at the user data pointer. WTF? Should we use it for that?
|
2022-11-27 04:45:16 +00:00
|
|
|
f := pointer.Restore(fkey).(ForeignFunction)
|
|
|
|
args := make([]*Value, 0, argc)
|
|
|
|
argvSlice := unsafe.Slice(argv, argc)
|
|
|
|
for i := range argvSlice {
|
|
|
|
args = append(args, (*Value)(argvSlice[i]))
|
|
|
|
}
|
|
|
|
return C.int(f.Call((*Context)(ctx), args))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (vm *VM) CreateFunction(name string, f ForeignFunction) error {
|
|
|
|
cname := C.CString(name)
|
|
|
|
defer C.free(unsafe.Pointer(cname))
|
|
|
|
|
|
|
|
fkey := pointer.Save(f)
|
|
|
|
vm.userFunctionKeys = append(vm.userFunctionKeys, fkey)
|
|
|
|
|
|
|
|
return newError(C.vm_create_function(vm.ptr, cname, fkey))
|
|
|
|
}
|
2022-11-27 16:32:17 +00:00
|
|
|
|
|
|
|
// TODO (*VM).DeleteFunction
|
|
|
|
// TODO (*VM).CreateConstant
|
|
|
|
// TODO (*VM).DeleteConstant
|