177 lines
3.8 KiB
Go
177 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dop251/goja"
|
|
)
|
|
|
|
// JsExecResult JS 执行结果
|
|
type JsExecResult struct {
|
|
Output string `json:"output"` // 控制台输出
|
|
Result string `json:"result"` // 返回值
|
|
Error string `json:"error"` // 错误信息
|
|
Duration string `json:"duration"` // 执行耗时
|
|
Success bool `json:"success"` // 是否成功
|
|
}
|
|
|
|
// consoleLog 存储 console.log 输出
|
|
type consoleLogger struct {
|
|
logs []string
|
|
}
|
|
|
|
func (c *consoleLogger) log(args ...interface{}) {
|
|
c.logs = append(c.logs, fmt.Sprint(args...))
|
|
}
|
|
|
|
func (c *consoleLogger) logFn(call goja.FunctionCall) goja.Value {
|
|
var args []interface{}
|
|
for _, arg := range call.Arguments {
|
|
args = append(args, arg.Export())
|
|
}
|
|
c.logs = append(c.logs, fmt.Sprint(args...))
|
|
return goja.Undefined()
|
|
}
|
|
|
|
// ExecuteJs 执行 JavaScript 代码
|
|
func ExecuteJs(code string) *JsExecResult {
|
|
result := &JsExecResult{
|
|
Success: false,
|
|
}
|
|
|
|
if strings.TrimSpace(code) == "" {
|
|
result.Error = "代码不能为空"
|
|
return result
|
|
}
|
|
|
|
start := time.Now()
|
|
|
|
// 创建 JS 虚拟机
|
|
vm := goja.New()
|
|
|
|
// 创建日志记录器
|
|
logger := &consoleLogger{
|
|
logs: make([]string, 0),
|
|
}
|
|
|
|
// 注入 console 对象
|
|
consoleObj := vm.NewObject()
|
|
consoleObj.Set("log", logger.logFn)
|
|
consoleObj.Set("info", logger.logFn)
|
|
consoleObj.Set("warn", logger.logFn)
|
|
consoleObj.Set("error", logger.logFn)
|
|
vm.Set("console", consoleObj)
|
|
|
|
// 注入常用全局函数
|
|
vm.Set("parseInt", parseInt)
|
|
vm.Set("parseFloat", parseFloat)
|
|
vm.Set("isNaN", isNaN)
|
|
vm.Set("isFinite", isFinite)
|
|
vm.Set("String", vm.Get("String"))
|
|
vm.Set("Number", vm.Get("Number"))
|
|
vm.Set("Boolean", vm.Get("Boolean"))
|
|
vm.Set("Array", vm.Get("Array"))
|
|
vm.Set("Object", vm.Get("Object"))
|
|
vm.Set("JSON", vm.Get("JSON"))
|
|
vm.Set("Math", vm.Get("Math"))
|
|
vm.Set("Date", vm.Get("Date"))
|
|
vm.Set("RegExp", vm.Get("RegExp"))
|
|
|
|
// 执行代码
|
|
val, err := vm.RunString(code)
|
|
if err != nil {
|
|
result.Error = err.Error()
|
|
result.Duration = time.Since(start).String()
|
|
result.Output = strings.Join(logger.logs, "\n")
|
|
return result
|
|
}
|
|
|
|
// 获取返回值
|
|
resultVal := ""
|
|
if val != nil && !goja.IsUndefined(val) && !goja.IsNull(val) {
|
|
resultVal = fmt.Sprintf("%v", val.Export())
|
|
}
|
|
|
|
result.Output = strings.Join(logger.logs, "\n")
|
|
result.Result = resultVal
|
|
result.Duration = time.Since(start).String()
|
|
result.Success = true
|
|
|
|
return result
|
|
}
|
|
|
|
// parseInt 解析整数
|
|
func parseInt(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
return goja.NaN()
|
|
}
|
|
|
|
arg := call.Arguments[0].String()
|
|
var radix int64 = 10
|
|
if len(call.Arguments) > 1 {
|
|
radix = call.Arguments[1].ToInteger()
|
|
}
|
|
|
|
var result int64
|
|
var err error
|
|
|
|
switch radix {
|
|
case 16:
|
|
_, err = fmt.Sscanf(arg, "%x", &result)
|
|
case 8:
|
|
_, err = fmt.Sscanf(arg, "%o", &result)
|
|
default:
|
|
_, err = fmt.Sscanf(arg, "%d", &result)
|
|
}
|
|
|
|
if err != nil {
|
|
return goja.NaN()
|
|
}
|
|
|
|
return goja.New().ToValue(result)
|
|
}
|
|
|
|
// parseFloat 解析浮点数
|
|
func parseFloat(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
return goja.NaN()
|
|
}
|
|
|
|
arg := call.Arguments[0].String()
|
|
var result float64
|
|
_, err := fmt.Sscanf(arg, "%f", &result)
|
|
if err != nil {
|
|
return goja.NaN()
|
|
}
|
|
|
|
return goja.New().ToValue(result)
|
|
}
|
|
|
|
// isNaN 检查是否为 NaN
|
|
func isNaN(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
return goja.New().ToValue(true)
|
|
}
|
|
|
|
val := call.Arguments[0].ToNumber()
|
|
if goja.IsNaN(val) {
|
|
return goja.New().ToValue(true)
|
|
}
|
|
return goja.New().ToValue(false)
|
|
}
|
|
|
|
// isFinite 检查是否为有限数
|
|
func isFinite(call goja.FunctionCall) goja.Value {
|
|
if len(call.Arguments) < 1 {
|
|
return goja.New().ToValue(false)
|
|
}
|
|
|
|
val := call.Arguments[0].ToNumber()
|
|
if goja.IsNaN(val) || goja.IsInfinity(val) {
|
|
return goja.New().ToValue(false)
|
|
}
|
|
return goja.New().ToValue(true)
|
|
}
|