module.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package console
  2. import (
  3. "bytes"
  4. "context"
  5. "github.com/sagernet/sing-box/script/modules/require"
  6. "github.com/sagernet/sing/common/logger"
  7. "github.com/dop251/goja"
  8. )
  9. const ModuleName = "console"
  10. type Console struct {
  11. vm *goja.Runtime
  12. }
  13. func (c *Console) log(ctx context.Context, p func(ctx context.Context, values ...any)) func(goja.FunctionCall) goja.Value {
  14. return func(call goja.FunctionCall) goja.Value {
  15. var buffer bytes.Buffer
  16. var format string
  17. if arg := call.Argument(0); !goja.IsUndefined(arg) {
  18. format = arg.String()
  19. }
  20. var args []goja.Value
  21. if len(call.Arguments) > 0 {
  22. args = call.Arguments[1:]
  23. }
  24. c.Format(&buffer, format, args...)
  25. p(ctx, buffer.String())
  26. return nil
  27. }
  28. }
  29. func (c *Console) Format(b *bytes.Buffer, f string, args ...goja.Value) {
  30. pct := false
  31. argNum := 0
  32. for _, chr := range f {
  33. if pct {
  34. if argNum < len(args) {
  35. if c.format(chr, args[argNum], b) {
  36. argNum++
  37. }
  38. } else {
  39. b.WriteByte('%')
  40. b.WriteRune(chr)
  41. }
  42. pct = false
  43. } else {
  44. if chr == '%' {
  45. pct = true
  46. } else {
  47. b.WriteRune(chr)
  48. }
  49. }
  50. }
  51. for _, arg := range args[argNum:] {
  52. b.WriteByte(' ')
  53. b.WriteString(arg.String())
  54. }
  55. }
  56. func (c *Console) format(f rune, val goja.Value, w *bytes.Buffer) bool {
  57. switch f {
  58. case 's':
  59. w.WriteString(val.String())
  60. case 'd':
  61. w.WriteString(val.ToNumber().String())
  62. case 'j':
  63. if json, ok := c.vm.Get("JSON").(*goja.Object); ok {
  64. if stringify, ok := goja.AssertFunction(json.Get("stringify")); ok {
  65. res, err := stringify(json, val)
  66. if err != nil {
  67. panic(err)
  68. }
  69. w.WriteString(res.String())
  70. }
  71. }
  72. case '%':
  73. w.WriteByte('%')
  74. return false
  75. default:
  76. w.WriteByte('%')
  77. w.WriteRune(f)
  78. return false
  79. }
  80. return true
  81. }
  82. func Require(ctx context.Context, logger logger.ContextLogger) require.ModuleLoader {
  83. return func(runtime *goja.Runtime, module *goja.Object) {
  84. c := &Console{
  85. vm: runtime,
  86. }
  87. o := module.Get("exports").(*goja.Object)
  88. o.Set("log", c.log(ctx, logger.DebugContext))
  89. o.Set("error", c.log(ctx, logger.ErrorContext))
  90. o.Set("warn", c.log(ctx, logger.WarnContext))
  91. o.Set("info", c.log(ctx, logger.InfoContext))
  92. o.Set("debug", c.log(ctx, logger.DebugContext))
  93. }
  94. }
  95. func Enable(runtime *goja.Runtime) {
  96. runtime.Set("console", require.Require(runtime, ModuleName))
  97. }