go.ketch.com/lib/goja@v0.0.1/builtin_promise.go (about)

     1  package goja
     2  
     3  import (
     4  	"go.ketch.com/lib/goja/unistring"
     5  	"reflect"
     6  )
     7  
     8  type PromiseState int
     9  type PromiseRejectionOperation int
    10  
    11  type promiseReactionType int
    12  
    13  const (
    14  	PromiseStatePending PromiseState = iota
    15  	PromiseStateFulfilled
    16  	PromiseStateRejected
    17  )
    18  
    19  const (
    20  	PromiseRejectionReject PromiseRejectionOperation = iota
    21  	PromiseRejectionHandle
    22  )
    23  
    24  const (
    25  	promiseReactionFulfill promiseReactionType = iota
    26  	promiseReactionReject
    27  )
    28  
    29  type PromiseRejectionTracker func(p *Promise, operation PromiseRejectionOperation)
    30  
    31  type jobCallback struct {
    32  	callback func(FunctionCall) Value
    33  }
    34  
    35  type promiseCapability struct {
    36  	promise               *Object
    37  	resolveObj, rejectObj *Object
    38  }
    39  
    40  type promiseReaction struct {
    41  	capability *promiseCapability
    42  	typ        promiseReactionType
    43  	handler    *jobCallback
    44  }
    45  
    46  var typePromise = reflect.TypeOf((*Promise)(nil))
    47  
    48  // Promise is a Go wrapper around ECMAScript Promise. Calling Runtime.ToValue() on it
    49  // returns the underlying Object. Calling Export() on a Promise Object returns a Promise.
    50  //
    51  // Use Runtime.NewPromise() to create one. Calling Runtime.ToValue() on a zero object or nil returns null Value.
    52  //
    53  // WARNING: Instances of Promise are not goroutine-safe. See Runtime.NewPromise() for more details.
    54  type Promise struct {
    55  	baseObject
    56  	state            PromiseState
    57  	result           Value
    58  	fulfillReactions []*promiseReaction
    59  	rejectReactions  []*promiseReaction
    60  	handled          bool
    61  }
    62  
    63  func (p *Promise) State() PromiseState {
    64  	return p.state
    65  }
    66  
    67  func (p *Promise) Result() Value {
    68  	return p.result
    69  }
    70  
    71  func (p *Promise) toValue(r *Runtime) Value {
    72  	if p == nil || p.val == nil {
    73  		return _null
    74  	}
    75  	promise := p.val
    76  	if promise.runtime != r {
    77  		panic(r.NewTypeError("Illegal runtime transition of a Promise"))
    78  	}
    79  	return promise
    80  }
    81  
    82  func (p *Promise) createResolvingFunctions() (resolve, reject *Object) {
    83  	r := p.val.runtime
    84  	alreadyResolved := false
    85  	return p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
    86  			if alreadyResolved {
    87  				return _undefined
    88  			}
    89  			alreadyResolved = true
    90  			resolution := call.Argument(0)
    91  			if resolution.SameAs(p.val) {
    92  				return p.reject(r.NewTypeError("Promise self-resolution"))
    93  			}
    94  			if obj, ok := resolution.(*Object); ok {
    95  				var thenAction Value
    96  				ex := r.vm.try(func() {
    97  					thenAction = obj.self.getStr("then", nil)
    98  				})
    99  				if ex != nil {
   100  					return p.reject(ex.val)
   101  				}
   102  				if call, ok := assertCallable(thenAction); ok {
   103  					job := r.newPromiseResolveThenableJob(p, resolution, &jobCallback{callback: call})
   104  					r.enqueuePromiseJob(job)
   105  					return _undefined
   106  				}
   107  			}
   108  			return p.fulfill(resolution)
   109  		}, nil, "", nil, 1),
   110  		p.val.runtime.newNativeFunc(func(call FunctionCall) Value {
   111  			if alreadyResolved {
   112  				return _undefined
   113  			}
   114  			alreadyResolved = true
   115  			reason := call.Argument(0)
   116  			return p.reject(reason)
   117  		}, nil, "", nil, 1)
   118  }
   119  
   120  func (p *Promise) reject(reason Value) Value {
   121  	reactions := p.rejectReactions
   122  	p.result = reason
   123  	p.fulfillReactions, p.rejectReactions = nil, nil
   124  	p.state = PromiseStateRejected
   125  	r := p.val.runtime
   126  	if !p.handled {
   127  		r.trackPromiseRejection(p, PromiseRejectionReject)
   128  	}
   129  	r.triggerPromiseReactions(reactions, reason)
   130  	return _undefined
   131  }
   132  
   133  func (p *Promise) fulfill(value Value) Value {
   134  	reactions := p.fulfillReactions
   135  	p.result = value
   136  	p.fulfillReactions, p.rejectReactions = nil, nil
   137  	p.state = PromiseStateFulfilled
   138  	p.val.runtime.triggerPromiseReactions(reactions, value)
   139  	return _undefined
   140  }
   141  
   142  func (p *Promise) exportType() reflect.Type {
   143  	return typePromise
   144  }
   145  
   146  func (p *Promise) export(*objectExportCtx) interface{} {
   147  	return p
   148  }
   149  
   150  func (r *Runtime) newPromiseResolveThenableJob(p *Promise, thenable Value, then *jobCallback) func() {
   151  	return func() {
   152  		resolve, reject := p.createResolvingFunctions()
   153  		ex := r.vm.try(func() {
   154  			r.callJobCallback(then, thenable, resolve, reject)
   155  		})
   156  		if ex != nil {
   157  			if fn, ok := reject.self.assertCallable(); ok {
   158  				fn(FunctionCall{Arguments: []Value{ex.val}})
   159  			}
   160  		}
   161  	}
   162  }
   163  
   164  func (r *Runtime) enqueuePromiseJob(job func()) {
   165  	r.jobQueue = append(r.jobQueue, job)
   166  }
   167  
   168  func (r *Runtime) triggerPromiseReactions(reactions []*promiseReaction, argument Value) {
   169  	for _, reaction := range reactions {
   170  		r.enqueuePromiseJob(r.newPromiseReactionJob(reaction, argument))
   171  	}
   172  }
   173  
   174  func (r *Runtime) newPromiseReactionJob(reaction *promiseReaction, argument Value) func() {
   175  	return func() {
   176  		var handlerResult Value
   177  		fulfill := false
   178  		if reaction.handler == nil {
   179  			handlerResult = argument
   180  			if reaction.typ == promiseReactionFulfill {
   181  				fulfill = true
   182  			}
   183  		} else {
   184  			ex := r.vm.try(func() {
   185  				handlerResult = r.callJobCallback(reaction.handler, _undefined, argument)
   186  				fulfill = true
   187  			})
   188  			if ex != nil {
   189  				handlerResult = ex.val
   190  			}
   191  		}
   192  		if reaction.capability != nil {
   193  			if fulfill {
   194  				reaction.capability.resolve(handlerResult)
   195  			} else {
   196  				reaction.capability.reject(handlerResult)
   197  			}
   198  		}
   199  	}
   200  }
   201  
   202  func (r *Runtime) newPromise(proto *Object) *Promise {
   203  	o := &Object{runtime: r}
   204  
   205  	po := &Promise{}
   206  	po.class = classPromise
   207  	po.val = o
   208  	po.extensible = true
   209  	o.self = po
   210  	po.prototype = proto
   211  	po.init()
   212  	return po
   213  }
   214  
   215  func (r *Runtime) builtin_newPromise(args []Value, newTarget *Object) *Object {
   216  	if newTarget == nil {
   217  		panic(r.needNew("Promise"))
   218  	}
   219  	var arg0 Value
   220  	if len(args) > 0 {
   221  		arg0 = args[0]
   222  	}
   223  	executor := r.toCallable(arg0)
   224  
   225  	proto := r.getPrototypeFromCtor(newTarget, r.global.Promise, r.global.PromisePrototype)
   226  	po := r.newPromise(proto)
   227  
   228  	resolve, reject := po.createResolvingFunctions()
   229  	ex := r.vm.try(func() {
   230  		executor(FunctionCall{Arguments: []Value{resolve, reject}})
   231  	})
   232  	if ex != nil {
   233  		if fn, ok := reject.self.assertCallable(); ok {
   234  			fn(FunctionCall{Arguments: []Value{ex.val}})
   235  		}
   236  	}
   237  	return po.val
   238  }
   239  
   240  func (r *Runtime) promiseProto_then(call FunctionCall) Value {
   241  	thisObj := r.toObject(call.This)
   242  	if p, ok := thisObj.self.(*Promise); ok {
   243  		c := r.speciesConstructorObj(thisObj, r.global.Promise)
   244  		resultCapability := r.newPromiseCapability(c)
   245  		return r.performPromiseThen(p, call.Argument(0), call.Argument(1), resultCapability)
   246  	}
   247  	panic(r.NewTypeError("Method Promise.prototype.then called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj})))
   248  }
   249  
   250  func (r *Runtime) newPromiseCapability(c *Object) *promiseCapability {
   251  	pcap := new(promiseCapability)
   252  	if c == r.global.Promise {
   253  		p := r.newPromise(r.global.PromisePrototype)
   254  		pcap.resolveObj, pcap.rejectObj = p.createResolvingFunctions()
   255  		pcap.promise = p.val
   256  	} else {
   257  		var resolve, reject Value
   258  		executor := r.newNativeFunc(func(call FunctionCall) Value {
   259  			if resolve != nil {
   260  				panic(r.NewTypeError("resolve is already set"))
   261  			}
   262  			if reject != nil {
   263  				panic(r.NewTypeError("reject is already set"))
   264  			}
   265  			if arg := call.Argument(0); arg != _undefined {
   266  				resolve = arg
   267  			}
   268  			if arg := call.Argument(1); arg != _undefined {
   269  				reject = arg
   270  			}
   271  			return nil
   272  		}, nil, "", nil, 2)
   273  		pcap.promise = r.toConstructor(c)([]Value{executor}, c)
   274  		pcap.resolveObj = r.toObject(resolve)
   275  		r.toCallable(pcap.resolveObj) // make sure it's callable
   276  		pcap.rejectObj = r.toObject(reject)
   277  		r.toCallable(pcap.rejectObj)
   278  	}
   279  	return pcap
   280  }
   281  
   282  func (r *Runtime) performPromiseThen(p *Promise, onFulfilled, onRejected Value, resultCapability *promiseCapability) Value {
   283  	var onFulfilledJobCallback, onRejectedJobCallback *jobCallback
   284  	if f, ok := assertCallable(onFulfilled); ok {
   285  		onFulfilledJobCallback = &jobCallback{callback: f}
   286  	}
   287  	if f, ok := assertCallable(onRejected); ok {
   288  		onRejectedJobCallback = &jobCallback{callback: f}
   289  	}
   290  	fulfillReaction := &promiseReaction{
   291  		capability: resultCapability,
   292  		typ:        promiseReactionFulfill,
   293  		handler:    onFulfilledJobCallback,
   294  	}
   295  	rejectReaction := &promiseReaction{
   296  		capability: resultCapability,
   297  		typ:        promiseReactionReject,
   298  		handler:    onRejectedJobCallback,
   299  	}
   300  	switch p.state {
   301  	case PromiseStatePending:
   302  		p.fulfillReactions = append(p.fulfillReactions, fulfillReaction)
   303  		p.rejectReactions = append(p.rejectReactions, rejectReaction)
   304  	case PromiseStateFulfilled:
   305  		r.enqueuePromiseJob(r.newPromiseReactionJob(fulfillReaction, p.result))
   306  	default:
   307  		reason := p.result
   308  		if !p.handled {
   309  			r.trackPromiseRejection(p, PromiseRejectionHandle)
   310  		}
   311  		r.enqueuePromiseJob(r.newPromiseReactionJob(rejectReaction, reason))
   312  	}
   313  	p.handled = true
   314  	if resultCapability == nil {
   315  		return _undefined
   316  	}
   317  	return resultCapability.promise
   318  }
   319  
   320  func (r *Runtime) promiseProto_catch(call FunctionCall) Value {
   321  	return r.invoke(call.This, "then", _undefined, call.Argument(0))
   322  }
   323  
   324  func (r *Runtime) promiseResolve(c *Object, x Value) *Object {
   325  	if obj, ok := x.(*Object); ok {
   326  		xConstructor := nilSafe(obj.self.getStr("constructor", nil))
   327  		if xConstructor.SameAs(c) {
   328  			return obj
   329  		}
   330  	}
   331  	pcap := r.newPromiseCapability(c)
   332  	pcap.resolve(x)
   333  	return pcap.promise
   334  }
   335  
   336  func (r *Runtime) promiseProto_finally(call FunctionCall) Value {
   337  	promise := r.toObject(call.This)
   338  	c := r.speciesConstructorObj(promise, r.global.Promise)
   339  	onFinally := call.Argument(0)
   340  	var thenFinally, catchFinally Value
   341  	if onFinallyFn, ok := assertCallable(onFinally); !ok {
   342  		thenFinally, catchFinally = onFinally, onFinally
   343  	} else {
   344  		thenFinally = r.newNativeFunc(func(call FunctionCall) Value {
   345  			value := call.Argument(0)
   346  			result := onFinallyFn(FunctionCall{})
   347  			promise := r.promiseResolve(c, result)
   348  			valueThunk := r.newNativeFunc(func(call FunctionCall) Value {
   349  				return value
   350  			}, nil, "", nil, 0)
   351  			return r.invoke(promise, "then", valueThunk)
   352  		}, nil, "", nil, 1)
   353  
   354  		catchFinally = r.newNativeFunc(func(call FunctionCall) Value {
   355  			reason := call.Argument(0)
   356  			result := onFinallyFn(FunctionCall{})
   357  			promise := r.promiseResolve(c, result)
   358  			thrower := r.newNativeFunc(func(call FunctionCall) Value {
   359  				panic(reason)
   360  			}, nil, "", nil, 0)
   361  			return r.invoke(promise, "then", thrower)
   362  		}, nil, "", nil, 1)
   363  	}
   364  	return r.invoke(promise, "then", thenFinally, catchFinally)
   365  }
   366  
   367  func (pcap *promiseCapability) resolve(result Value) {
   368  	pcap.promise.runtime.toCallable(pcap.resolveObj)(FunctionCall{Arguments: []Value{result}})
   369  }
   370  
   371  func (pcap *promiseCapability) reject(reason Value) {
   372  	pcap.promise.runtime.toCallable(pcap.rejectObj)(FunctionCall{Arguments: []Value{reason}})
   373  }
   374  
   375  func (pcap *promiseCapability) try(f func()) bool {
   376  	ex := pcap.promise.runtime.vm.try(f)
   377  	if ex != nil {
   378  		pcap.reject(ex.val)
   379  		return false
   380  	}
   381  	return true
   382  }
   383  
   384  func (r *Runtime) promise_all(call FunctionCall) Value {
   385  	c := r.toObject(call.This)
   386  	pcap := r.newPromiseCapability(c)
   387  
   388  	pcap.try(func() {
   389  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   390  		iter := r.getIterator(call.Argument(0), nil)
   391  		var values []Value
   392  		remainingElementsCount := 1
   393  		iter.iterate(func(nextValue Value) {
   394  			index := len(values)
   395  			values = append(values, _undefined)
   396  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   397  			alreadyCalled := false
   398  			onFulfilled := r.newNativeFunc(func(call FunctionCall) Value {
   399  				if alreadyCalled {
   400  					return _undefined
   401  				}
   402  				alreadyCalled = true
   403  				values[index] = call.Argument(0)
   404  				remainingElementsCount--
   405  				if remainingElementsCount == 0 {
   406  					pcap.resolve(r.newArrayValues(values))
   407  				}
   408  				return _undefined
   409  			}, nil, "", nil, 1)
   410  			remainingElementsCount++
   411  			r.invoke(nextPromise, "then", onFulfilled, pcap.rejectObj)
   412  		})
   413  		remainingElementsCount--
   414  		if remainingElementsCount == 0 {
   415  			pcap.resolve(r.newArrayValues(values))
   416  		}
   417  	})
   418  	return pcap.promise
   419  }
   420  
   421  func (r *Runtime) promise_allSettled(call FunctionCall) Value {
   422  	c := r.toObject(call.This)
   423  	pcap := r.newPromiseCapability(c)
   424  
   425  	pcap.try(func() {
   426  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   427  		iter := r.getIterator(call.Argument(0), nil)
   428  		var values []Value
   429  		remainingElementsCount := 1
   430  		iter.iterate(func(nextValue Value) {
   431  			index := len(values)
   432  			values = append(values, _undefined)
   433  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   434  			alreadyCalled := false
   435  			reaction := func(status Value, valueKey unistring.String) *Object {
   436  				return r.newNativeFunc(func(call FunctionCall) Value {
   437  					if alreadyCalled {
   438  						return _undefined
   439  					}
   440  					alreadyCalled = true
   441  					obj := r.NewObject()
   442  					obj.self._putProp("status", status, true, true, true)
   443  					obj.self._putProp(valueKey, call.Argument(0), true, true, true)
   444  					values[index] = obj
   445  					remainingElementsCount--
   446  					if remainingElementsCount == 0 {
   447  						pcap.resolve(r.newArrayValues(values))
   448  					}
   449  					return _undefined
   450  				}, nil, "", nil, 1)
   451  			}
   452  			onFulfilled := reaction(asciiString("fulfilled"), "value")
   453  			onRejected := reaction(asciiString("rejected"), "reason")
   454  			remainingElementsCount++
   455  			r.invoke(nextPromise, "then", onFulfilled, onRejected)
   456  		})
   457  		remainingElementsCount--
   458  		if remainingElementsCount == 0 {
   459  			pcap.resolve(r.newArrayValues(values))
   460  		}
   461  	})
   462  	return pcap.promise
   463  }
   464  
   465  func (r *Runtime) promise_any(call FunctionCall) Value {
   466  	c := r.toObject(call.This)
   467  	pcap := r.newPromiseCapability(c)
   468  
   469  	pcap.try(func() {
   470  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   471  		iter := r.getIterator(call.Argument(0), nil)
   472  		var errors []Value
   473  		remainingElementsCount := 1
   474  		iter.iterate(func(nextValue Value) {
   475  			index := len(errors)
   476  			errors = append(errors, _undefined)
   477  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   478  			alreadyCalled := false
   479  			onRejected := r.newNativeFunc(func(call FunctionCall) Value {
   480  				if alreadyCalled {
   481  					return _undefined
   482  				}
   483  				alreadyCalled = true
   484  				errors[index] = call.Argument(0)
   485  				remainingElementsCount--
   486  				if remainingElementsCount == 0 {
   487  					_error := r.builtin_new(r.global.AggregateError, nil)
   488  					_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
   489  					pcap.reject(_error)
   490  				}
   491  				return _undefined
   492  			}, nil, "", nil, 1)
   493  
   494  			remainingElementsCount++
   495  			r.invoke(nextPromise, "then", pcap.resolveObj, onRejected)
   496  		})
   497  		remainingElementsCount--
   498  		if remainingElementsCount == 0 {
   499  			_error := r.builtin_new(r.global.AggregateError, nil)
   500  			_error.self._putProp("errors", r.newArrayValues(errors), true, false, true)
   501  			pcap.reject(_error)
   502  		}
   503  	})
   504  	return pcap.promise
   505  }
   506  
   507  func (r *Runtime) promise_race(call FunctionCall) Value {
   508  	c := r.toObject(call.This)
   509  	pcap := r.newPromiseCapability(c)
   510  
   511  	pcap.try(func() {
   512  		promiseResolve := r.toCallable(c.self.getStr("resolve", nil))
   513  		iter := r.getIterator(call.Argument(0), nil)
   514  		iter.iterate(func(nextValue Value) {
   515  			nextPromise := promiseResolve(FunctionCall{This: c, Arguments: []Value{nextValue}})
   516  			r.invoke(nextPromise, "then", pcap.resolveObj, pcap.rejectObj)
   517  		})
   518  	})
   519  	return pcap.promise
   520  }
   521  
   522  func (r *Runtime) promise_reject(call FunctionCall) Value {
   523  	pcap := r.newPromiseCapability(r.toObject(call.This))
   524  	pcap.reject(call.Argument(0))
   525  	return pcap.promise
   526  }
   527  
   528  func (r *Runtime) promise_resolve(call FunctionCall) Value {
   529  	return r.promiseResolve(r.toObject(call.This), call.Argument(0))
   530  }
   531  
   532  func (r *Runtime) createPromiseProto(val *Object) objectImpl {
   533  	o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
   534  	o._putProp("constructor", r.global.Promise, true, false, true)
   535  
   536  	o._putProp("catch", r.newNativeFunc(r.promiseProto_catch, nil, "catch", nil, 1), true, false, true)
   537  	o._putProp("finally", r.newNativeFunc(r.promiseProto_finally, nil, "finally", nil, 1), true, false, true)
   538  	o._putProp("then", r.newNativeFunc(r.promiseProto_then, nil, "then", nil, 2), true, false, true)
   539  
   540  	o._putSym(SymToStringTag, valueProp(asciiString(classPromise), false, false, true))
   541  
   542  	return o
   543  }
   544  
   545  func (r *Runtime) createPromise(val *Object) objectImpl {
   546  	o := r.newNativeConstructOnly(val, r.builtin_newPromise, r.global.PromisePrototype, "Promise", 1)
   547  
   548  	o._putProp("all", r.newNativeFunc(r.promise_all, nil, "all", nil, 1), true, false, true)
   549  	o._putProp("allSettled", r.newNativeFunc(r.promise_allSettled, nil, "allSettled", nil, 1), true, false, true)
   550  	o._putProp("any", r.newNativeFunc(r.promise_any, nil, "any", nil, 1), true, false, true)
   551  	o._putProp("race", r.newNativeFunc(r.promise_race, nil, "race", nil, 1), true, false, true)
   552  	o._putProp("reject", r.newNativeFunc(r.promise_reject, nil, "reject", nil, 1), true, false, true)
   553  	o._putProp("resolve", r.newNativeFunc(r.promise_resolve, nil, "resolve", nil, 1), true, false, true)
   554  
   555  	r.putSpeciesReturnThis(o)
   556  
   557  	return o
   558  }
   559  
   560  func (r *Runtime) initPromise() {
   561  	r.global.PromisePrototype = r.newLazyObject(r.createPromiseProto)
   562  	r.global.Promise = r.newLazyObject(r.createPromise)
   563  
   564  	r.addToGlobal("Promise", r.global.Promise)
   565  }
   566  
   567  func (r *Runtime) wrapPromiseReaction(fObj *Object) func(interface{}) {
   568  	f, _ := AssertFunction(fObj)
   569  	return func(x interface{}) {
   570  		_, _ = f(nil, r.ToValue(x))
   571  	}
   572  }
   573  
   574  // NewPromise creates and returns a Promise and resolving functions for it.
   575  //
   576  // WARNING: The returned values are not goroutine-safe and must not be called in parallel with VM running.
   577  // In order to make use of this method you need an event loop such as the one in goja_nodejs (https://github.com/dop251/goja_nodejs)
   578  // where it can be used like this:
   579  //
   580  //	 loop := NewEventLoop()
   581  //	 loop.Start()
   582  //	 defer loop.Stop()
   583  //	 loop.RunOnLoop(func(vm *goja.Runtime) {
   584  //			p, resolve, _ := vm.NewPromise()
   585  //			vm.Set("p", p)
   586  //	     go func() {
   587  //	  		time.Sleep(500 * time.Millisecond)   // or perform any other blocking operation
   588  //				loop.RunOnLoop(func(*goja.Runtime) { // resolve() must be called on the loop, cannot call it here
   589  //					resolve(result)
   590  //				})
   591  //			}()
   592  //	 }
   593  func (r *Runtime) NewPromise() (promise *Promise, resolve func(result interface{}), reject func(reason interface{})) {
   594  	p := r.newPromise(r.global.PromisePrototype)
   595  	resolveF, rejectF := p.createResolvingFunctions()
   596  	return p, r.wrapPromiseReaction(resolveF), r.wrapPromiseReaction(rejectF)
   597  }
   598  
   599  // SetPromiseRejectionTracker registers a function that will be called in two scenarios: when a promise is rejected
   600  // without any handlers (with operation argument set to PromiseRejectionReject), and when a handler is added to a
   601  // rejected promise for the first time (with operation argument set to PromiseRejectionHandle).
   602  //
   603  // Setting a tracker replaces any existing one. Setting it to nil disables the functionality.
   604  //
   605  // See https://tc39.es/ecma262/#sec-host-promise-rejection-tracker for more details.
   606  func (r *Runtime) SetPromiseRejectionTracker(tracker PromiseRejectionTracker) {
   607  	r.promiseRejectionTracker = tracker
   608  }