|
@@ -4,7 +4,6 @@ import (
|
|
|
"bytes"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
- "github.com/dop251/goja/file"
|
|
|
"go/ast"
|
|
|
"hash/maphash"
|
|
|
"math"
|
|
@@ -18,6 +17,7 @@ import (
|
|
|
"golang.org/x/text/collate"
|
|
|
|
|
|
js_ast "github.com/dop251/goja/ast"
|
|
|
+ "github.com/dop251/goja/file"
|
|
|
"github.com/dop251/goja/parser"
|
|
|
"github.com/dop251/goja/unistring"
|
|
|
)
|
|
@@ -2346,36 +2346,18 @@ func (r *Runtime) New(construct Value, args ...Value) (o *Object, err error) {
|
|
|
type Callable func(this Value, args ...Value) (Value, error)
|
|
|
|
|
|
// AssertFunction checks if the Value is a function and returns a Callable.
|
|
|
+// Note, for classes this returns a callable and a 'true', however calling it will always result in a TypeError.
|
|
|
+// For classes use AssertConstructor().
|
|
|
func AssertFunction(v Value) (Callable, bool) {
|
|
|
if obj, ok := v.(*Object); ok {
|
|
|
if f, ok := obj.self.assertCallable(); ok {
|
|
|
return func(this Value, args ...Value) (ret Value, err error) {
|
|
|
- defer func() {
|
|
|
- if x := recover(); x != nil {
|
|
|
- if ex, ok := x.(*uncatchableException); ok {
|
|
|
- err = ex.err
|
|
|
- if len(obj.runtime.vm.callStack) == 0 {
|
|
|
- obj.runtime.leaveAbrupt()
|
|
|
- }
|
|
|
- } else {
|
|
|
- panic(x)
|
|
|
- }
|
|
|
- }
|
|
|
- }()
|
|
|
- ex := obj.runtime.vm.try(func() {
|
|
|
+ err = obj.runtime.runWrapped(func() {
|
|
|
ret = f(FunctionCall{
|
|
|
This: this,
|
|
|
Arguments: args,
|
|
|
})
|
|
|
})
|
|
|
- if ex != nil {
|
|
|
- err = ex
|
|
|
- }
|
|
|
- vm := obj.runtime.vm
|
|
|
- vm.clearStack()
|
|
|
- if len(vm.callStack) == 0 {
|
|
|
- obj.runtime.leave()
|
|
|
- }
|
|
|
return
|
|
|
}, true
|
|
|
}
|
|
@@ -2383,6 +2365,49 @@ func AssertFunction(v Value) (Callable, bool) {
|
|
|
return nil, false
|
|
|
}
|
|
|
|
|
|
+// Constructor is a type that can be used to call constructors. The first argument (newTarget) can be nil
|
|
|
+// which sets it to the constructor function itself.
|
|
|
+type Constructor func(newTarget *Object, args ...Value) (*Object, error)
|
|
|
+
|
|
|
+// AssertConstructor checks if the Value is a constructor and returns a Constructor.
|
|
|
+func AssertConstructor(v Value) (Constructor, bool) {
|
|
|
+ if obj, ok := v.(*Object); ok {
|
|
|
+ if ctor := obj.self.assertConstructor(); ctor != nil {
|
|
|
+ return func(newTarget *Object, args ...Value) (ret *Object, err error) {
|
|
|
+ err = obj.runtime.runWrapped(func() {
|
|
|
+ ret = ctor(args, newTarget)
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil, false
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Runtime) runWrapped(f func()) (err error) {
|
|
|
+ defer func() {
|
|
|
+ if x := recover(); x != nil {
|
|
|
+ if ex, ok := x.(*uncatchableException); ok {
|
|
|
+ err = ex.err
|
|
|
+ if len(r.vm.callStack) == 0 {
|
|
|
+ r.leaveAbrupt()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ panic(x)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ ex := r.vm.try(f)
|
|
|
+ if ex != nil {
|
|
|
+ err = ex
|
|
|
+ }
|
|
|
+ r.vm.clearStack()
|
|
|
+ if len(r.vm.callStack) == 0 {
|
|
|
+ r.leave()
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
// IsUndefined returns true if the supplied Value is undefined. Note, it checks against the real undefined, not
|
|
|
// against the global object's 'undefined' property.
|
|
|
func IsUndefined(v Value) bool {
|