| 27 | } |
| 28 | |
| 29 | func (c *compiled) Run(records *storage.Records, args []string) (ctx *wrapper.Context, raw []byte, err error) { |
| 30 | ret, err := func() (otto.Value, error) { |
| 31 | var anyValue interface{} |
| 32 | // prepare the context that the oracle will be able to use |
| 33 | // to signal errors and other specific states or events |
| 34 | ctx = wrapper.NewContext() |
| 35 | // define the arguments taking into account |
| 36 | // that some of them might be optional |
| 37 | for len(args) < c.argc { |
| 38 | args = append(args, "null") |
| 39 | } |
| 40 | // in order to avoid locking the global vm and make this |
| 41 | // basically single thread, we create a separate clone |
| 42 | // for each evaluation. |
| 43 | // NOTE: this will block until a vm is available from the pool. |
| 44 | vm := c.pool.Get() |
| 45 | defer vm.Release() |
| 46 | // define context and globals |
| 47 | vm.Set("records", wrapper.WrapRecords(records)) |
| 48 | vm.Set("ctx", ctx) |
| 49 | // define the arguments |
| 50 | for argIdx := 0; argIdx < c.argc; argIdx++ { |
| 51 | // unmarshal a typed value from the string value of |
| 52 | // the argument otherwise vm.Set will define everything |
| 53 | // as a string |
| 54 | argRaw := args[argIdx] |
| 55 | if err = json.Unmarshal([]byte(argRaw), &anyValue); err != nil { |
| 56 | // NOTE: this error condition is not covered by tests |
| 57 | // because I couldn't find a way to trigger it giving |
| 58 | // that the args list is made of simple strings. |
| 59 | return otto.NullValue(), fmt.Errorf("could not unmarshal value '%s': %s", argRaw, err) |
| 60 | } |
| 61 | vm.Set(c.args[argIdx], anyValue) |
| 62 | } |
| 63 | // evaluate the function call |
| 64 | return vm.Run(c.call) |
| 65 | }() |
| 66 | |
| 67 | if err != nil { |
| 68 | // do not marshal return value if there's an error |
| 69 | return ctx, nil, err |
| 70 | } else if ctx.IsError() { |
| 71 | // same goes for errors triggered within the oracle |
| 72 | return ctx, nil, errors.New(ctx.Message()) |
| 73 | } else if obj, err := ret.Export(); err != nil { |
| 74 | // or if we can't export its return value |
| 75 | // NOTE: this error condition is not covered by tests |
| 76 | // because I couldn't find a way to trigger it |
| 77 | return ctx, nil, err |
| 78 | } else if raw, err = json.Marshal(obj); err != nil { |
| 79 | // or if we can't marshal it to a raw buffer for transport |
| 80 | return ctx, nil, err |
| 81 | } |
| 82 | return ctx, raw, nil |
| 83 | } |