Skip to content

Commit

Permalink
make parseRemoteObject handle other kinds that can be nil correctly (#…
Browse files Browse the repository at this point in the history
…1260)

Besides `reflect.Pointer`, the value of the following kinds can be nil too:

- reflect.Chan
- reflect.Func
- reflect.Interface
- reflect.Map
- reflect.Slice

---------

Co-authored-by: luster <xxt@nosugartech.com>
  • Loading branch information
xueyc1f and luster committed Feb 20, 2023
1 parent b115f85 commit 1738691
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 17 deletions.
39 changes: 23 additions & 16 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ type EvaluateAction Action
// For all other cases, the result of the script will be returned "by value" (i.e.,
// JSON-encoded), and subsequently an attempt will be made to json.Unmarshal
// the script result to res. When the script result is "undefined" or "null",
// and the value that res points to is not a pointer, it returns [ErrJSUndefined]
// of [ErrJSNull] respectively.
// and the value that res points to can not be nil (only the value of a chan,
// func, interface, map, pointer, or slice can be nil), it returns [ErrJSUndefined]
// or [ErrJSNull] respectively.
func Evaluate(expression string, res interface{}, opts ...EvaluateOption) EvaluateAction {
return ActionFunc(func(ctx context.Context) error {
// set up parameters
Expand All @@ -55,8 +56,7 @@ func Evaluate(expression string, res interface{}, opts ...EvaluateOption) Evalua
return exp
}

err = parseRemoteObject(v, res)
return err
return parseRemoteObject(v, res)
})
}

Expand All @@ -75,23 +75,30 @@ func parseRemoteObject(v *runtime.RemoteObject, res interface{}) (err error) {
return
}

if v.Type == "undefined" || v.Value == nil {
value := v.Value
if value == nil {
rv := reflect.ValueOf(res)
// `res` should be a pointer. When the value that `res` points to is
// not a pointer, it can not be nil. In this case,
// return [ErrJSUndefined] or [ErrJSNull] respectively.
if rv.Kind() == reflect.Pointer && rv.Elem().Kind() != reflect.Pointer {
if v.Type == "undefined" {
return ErrJSUndefined
if rv.Kind() == reflect.Pointer {
switch rv.Elem().Kind() {
// Common kinds that can be nil.
case reflect.Pointer, reflect.Map, reflect.Slice:
// It's weird that res is a pointer to the following kinds,
// but they can be nil too.
case reflect.Chan, reflect.Func, reflect.Interface:
default:
// When the value that `res` points to can not be set to nil,
// return [ErrJSUndefined] or [ErrJSNull] respectively.
if v.Type == "undefined" {
return ErrJSUndefined
}
return ErrJSNull
}
return ErrJSNull
}
// Otherwise, change the value to the json literal null.
v.Value = []byte("null")
// Change the value to the json literal null to make json.Unmarshal happy.
value = []byte("null")
}

err = json.Unmarshal(v.Value, res)
return
return json.Unmarshal(value, res)
}

// EvaluateAsDevTools is an action that evaluates a JavaScript expression as
Expand Down
26 changes: 25 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ func ExampleEvaluate() {
}
}

// Accept undefined/nil result:
// Accept undefined/null result:
{
var val *int
if err := chromedp.Run(ctx,
Expand All @@ -649,6 +649,28 @@ func ExampleEvaluate() {
fmt.Println(val)
}

// Receive an array value:
{
var val []int
if err := chromedp.Run(ctx,
chromedp.Evaluate(`[1,2]`, &val),
); err != nil {
log.Fatal(err)
}
fmt.Println(val)
}

// Map and Slice accept undefined/null:
{
var val []int
if err := chromedp.Run(ctx,
chromedp.Evaluate(`null`, &val),
); err != nil {
log.Fatal(err)
}
fmt.Println("slice is nil:", val == nil)
}

// Receive the raw bytes:
{
var buf []byte
Expand Down Expand Up @@ -678,6 +700,8 @@ func ExampleEvaluate() {
// encountered an undefined value
// encountered a null value
// <nil>
// [1 2]
// slice is nil: true
// {}
// objectId is present
}

0 comments on commit 1738691

Please sign in to comment.