github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/func_test.go (about)

     1  package goja
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  )
     9  
    10  func TestFuncProto(t *testing.T) {
    11  	const SCRIPT = `
    12  	"use strict";
    13  	function A() {}
    14  	A.__proto__ = Object;
    15  	A.prototype = {};
    16  
    17  	function B() {}
    18  	B.__proto__ = Object.create(null);
    19  	var thrown = false;
    20  	try {
    21  		delete B.prototype;
    22  	} catch (e) {
    23  		thrown = e instanceof TypeError;
    24  	}
    25  	thrown;
    26  	`
    27  	testScript(SCRIPT, valueTrue, t)
    28  }
    29  
    30  func TestFuncPrototypeRedefine(t *testing.T) {
    31  	const SCRIPT = `
    32  	let thrown = false;
    33  	try {
    34  		Object.defineProperty(function() {}, "prototype", {
    35  			set: function(_value) {},
    36  		});
    37  	} catch (e) {
    38  		if (e instanceof TypeError) {
    39  			thrown = true;
    40  		} else {
    41  			throw e;
    42  		}
    43  	}
    44  	thrown;
    45  	`
    46  
    47  	testScript(SCRIPT, valueTrue, t)
    48  }
    49  
    50  func TestFuncExport(t *testing.T) {
    51  	vm := New()
    52  	typ := reflect.TypeOf((func(FunctionCall) Value)(nil))
    53  
    54  	f := func(expr string, t *testing.T) {
    55  		v, err := vm.RunString(expr)
    56  		if err != nil {
    57  			t.Fatal(err)
    58  		}
    59  		if actualTyp := v.ExportType(); actualTyp != typ {
    60  			t.Fatalf("Invalid export type: %v", actualTyp)
    61  		}
    62  		ev := v.Export()
    63  		if actualTyp := reflect.TypeOf(ev); actualTyp != typ {
    64  			t.Fatalf("Invalid export value: %v", ev)
    65  		}
    66  	}
    67  
    68  	t.Run("regular function", func(t *testing.T) {
    69  		f("(function() {})", t)
    70  	})
    71  
    72  	t.Run("arrow function", func(t *testing.T) {
    73  		f("(()=>{})", t)
    74  	})
    75  
    76  	t.Run("method", func(t *testing.T) {
    77  		f("({m() {}}).m", t)
    78  	})
    79  
    80  	t.Run("class", func(t *testing.T) {
    81  		f("(class {})", t)
    82  	})
    83  }
    84  
    85  func TestFuncWrapUnwrap(t *testing.T) {
    86  	vm := New()
    87  	f := func(a int, b string) bool {
    88  		return a > 0 && b != ""
    89  	}
    90  	var f1 func(int, string) bool
    91  	v := vm.ToValue(f)
    92  	if et := v.ExportType(); et != reflect.TypeOf(f1) {
    93  		t.Fatal(et)
    94  	}
    95  	err := vm.ExportTo(v, &f1)
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	if !f1(1, "a") {
   100  		t.Fatal("not true")
   101  	}
   102  }
   103  
   104  func TestWrappedFunc(t *testing.T) {
   105  	vm := New()
   106  	f := func(a int, b string) bool {
   107  		return a > 0 && b != ""
   108  	}
   109  	vm.Set("f", f)
   110  	const SCRIPT = `
   111  	assert.sameValue(typeof f, "function");
   112  	const s = f.toString()
   113  	assert(s.endsWith("TestWrappedFunc.func1() { [native code] }"), s);
   114  	assert(f(1, "a"));
   115  	assert(!f(0, ""));
   116  	`
   117  	vm.testScriptWithTestLib(SCRIPT, _undefined, t)
   118  }
   119  
   120  func TestWrappedFuncErrorPassthrough(t *testing.T) {
   121  	vm := New()
   122  	e := errors.New("test")
   123  	f := func(a int) error {
   124  		if a > 0 {
   125  			return e
   126  		}
   127  		return nil
   128  	}
   129  
   130  	var f1 func(a int64) error
   131  	err := vm.ExportTo(vm.ToValue(f), &f1)
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	if err := f1(1); err != e {
   136  		t.Fatal(err)
   137  	}
   138  }
   139  
   140  func ExampleAssertConstructor() {
   141  	vm := New()
   142  	res, err := vm.RunString(`
   143  		(class C {
   144  			constructor(x) {
   145  				this.x = x;
   146  			}
   147  		})
   148  	`)
   149  	if err != nil {
   150  		panic(err)
   151  	}
   152  	if ctor, ok := AssertConstructor(res); ok {
   153  		obj, err := ctor(nil, vm.ToValue("Test"))
   154  		if err != nil {
   155  			panic(err)
   156  		}
   157  		fmt.Print(obj.Get("x"))
   158  	} else {
   159  		panic("Not a constructor")
   160  	}
   161  	// Output: Test
   162  }
   163  
   164  type testAsyncCtx struct {
   165  	group    string
   166  	refCount int
   167  }
   168  
   169  type testAsyncContextTracker struct {
   170  	ctx     *testAsyncCtx
   171  	logFunc func(...interface{})
   172  	resumed bool
   173  }
   174  
   175  func (s *testAsyncContextTracker) Grab() interface{} {
   176  	ctx := s.ctx
   177  	if ctx != nil {
   178  		s.logFunc("Grab", ctx.group)
   179  		ctx.refCount++
   180  	}
   181  	return ctx
   182  }
   183  
   184  func (s *testAsyncContextTracker) Resumed(trackingObj interface{}) {
   185  	s.logFunc("Resumed", trackingObj)
   186  	if s.resumed {
   187  		panic("Nested Resumed() calls")
   188  	}
   189  	s.ctx = trackingObj.(*testAsyncCtx)
   190  	s.resumed = true
   191  }
   192  
   193  func (s *testAsyncContextTracker) releaseCtx() {
   194  	s.ctx.refCount--
   195  	if s.ctx.refCount < 0 {
   196  		panic("refCount < 0")
   197  	}
   198  	if s.ctx.refCount == 0 {
   199  		s.logFunc(s.ctx.group, "is finished")
   200  	}
   201  }
   202  
   203  func (s *testAsyncContextTracker) Exited() {
   204  	s.logFunc("Exited")
   205  	if s.ctx != nil {
   206  		s.releaseCtx()
   207  		s.ctx = nil
   208  	}
   209  	s.resumed = false
   210  }
   211  
   212  func TestAsyncContextTracker(t *testing.T) {
   213  	r := New()
   214  	var tracker testAsyncContextTracker
   215  	tracker.logFunc = t.Log
   216  
   217  	group := func(name string, asyncFunc func(FunctionCall) Value) Value {
   218  		prevCtx := tracker.ctx
   219  		defer func() {
   220  			t.Log("Returned", name)
   221  			tracker.releaseCtx()
   222  			tracker.ctx = prevCtx
   223  		}()
   224  		tracker.ctx = &testAsyncCtx{
   225  			group:    name,
   226  			refCount: 1,
   227  		}
   228  		t.Log("Set", name)
   229  		return asyncFunc(FunctionCall{})
   230  	}
   231  	r.SetAsyncContextTracker(&tracker)
   232  	r.Set("group", group)
   233  	r.Set("check", func(expectedGroup, msg string) {
   234  		var groupName string
   235  		if tracker.ctx != nil {
   236  			groupName = tracker.ctx.group
   237  		}
   238  		if groupName != expectedGroup {
   239  			t.Fatalf("Unexpected group (%q), expected %q in %s", groupName, expectedGroup, msg)
   240  		}
   241  		t.Log("In", msg)
   242  	})
   243  
   244  	t.Run("", func(t *testing.T) {
   245  		_, err := r.RunString(`
   246  		group("1", async () => {
   247  		  check("1", "line A");
   248  		  await 3;
   249  		  check("1", "line B");
   250  		  group("2", async () => {
   251  		     check("2", "line C");
   252  		     await 4;
   253  		     check("2", "line D");
   254  		 })
   255  		}).then(() => {
   256              check("", "line E");
   257  		})
   258  		`)
   259  		if err != nil {
   260  			t.Fatal(err)
   261  		}
   262  	})
   263  
   264  	t.Run("", func(t *testing.T) {
   265  		_, err := r.RunString(`
   266  		group("some", async () => {
   267  			check("some", "line A");
   268  		    (async () => {
   269  				check("some", "line B");
   270  		        await 1;
   271  				check("some", "line C");
   272  		        await 2;
   273  				check("some", "line D");
   274  		    })();
   275  			check("some", "line E");
   276  		});
   277  	`)
   278  		if err != nil {
   279  			t.Fatal(err)
   280  		}
   281  	})
   282  
   283  	t.Run("", func(t *testing.T) {
   284  		_, err := r.RunString(`
   285  	group("Main", async () => {
   286  		check("Main", "0.1");
   287  		await Promise.all([
   288  			group("A", async () => {
   289  				check("A", "1.1");
   290  				await 1;
   291  				check("A", "1.2");
   292  			}),
   293  			(async () => {
   294  				check("Main", "3.1");
   295  			})(),
   296  			group("B", async () => {
   297  				check("B", "2.1");
   298  				await 2;
   299  				check("B", "2.2");
   300  			})
   301  		]);
   302  		check("Main", "0.2");
   303  	});
   304  	`)
   305  		if err != nil {
   306  			t.Fatal(err)
   307  		}
   308  	})
   309  }