cmd, core, eth/tracers: support fancier js tracing (#15516)

* cmd, core, eth/tracers: support fancier js tracing

* eth, internal/web3ext: rework trace API, concurrency, chain tracing

* eth/tracers: add three more JavaScript tracers

* eth/tracers, vendor: swap ottovm to duktape for tracing

* core, eth, internal: finalize call tracer and needed extras

* eth, tests: prestate tracer, call test suite, rewinding

* vendor: fix windows builds for tracer js engine

* vendor: temporary duktape fix

* eth/tracers: fix up 4byte and evmdis tracer

* vendor: pull in latest duktape with my upstream fixes

* eth: fix some review comments

* eth: rename rewind to reexec to make it more obvious

* core/vm: terminate tracing using defers
This commit is contained in:
Péter Szilágyi
2017-12-21 13:56:11 +02:00
committed by GitHub
parent 1a5425779b
commit 5258785c81
64 changed files with 109115 additions and 685 deletions

21
vendor/gopkg.in/olebedev/go-duktape.v3/Gopkg.lock generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "v1"
name = "gopkg.in/check.v1"
packages = ["."]
revision = "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec"
[[projects]]
branch = "v3"
name = "gopkg.in/olebedev/go-duktape.v3"
packages = ["."]
revision = "391c1c40178e77a6003d889b96e0e41129aeb894"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "043f802c0b40e2622bf784443d3e3959f0d01e9a795e3bfe30a72060dec10c63"
solver-name = "gps-cdcl"
solver-version = 1

3
vendor/gopkg.in/olebedev/go-duktape.v3/Gopkg.toml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
[[constraint]]
branch = "v1"
name = "gopkg.in/check.v1"

21
vendor/gopkg.in/olebedev/go-duktape.v3/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Oleg Lebedev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

124
vendor/gopkg.in/olebedev/go-duktape.v3/README.md generated vendored Normal file
View File

@@ -0,0 +1,124 @@
# Duktape bindings for Go(Golang)
[![wercker status](https://app.wercker.com/status/3a5bb2e639a4b4efaf4c8bf7cab7442d/s "wercker status")](https://app.wercker.com/project/bykey/3a5bb2e639a4b4efaf4c8bf7cab7442d)
[![Travis status](https://travis-ci.org/olebedev/go-duktape.svg?branch=v3)](https://travis-ci.org/olebedev/go-duktape)
[![Appveyor status](https://ci.appveyor.com/api/projects/status/github/olebedev/go-duktape?branch=v3&svg=true)](https://ci.appveyor.com/project/olebedev/go-duktape/branch/v3)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/olebedev/go-duktape?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[Duktape](http://duktape.org/index.html) is a thin, embeddable javascript engine.
Most of the [api](http://duktape.org/api.html) is implemented.
The exceptions are listed [here](https://github.com/olebedev/go-duktape/blob/master/api.go#L1566).
### Usage
The package is fully go-getable, no need to install any external C libraries.
So, just type `go get gopkg.in/olebedev/go-duktape.v3` to install.
```go
package main
import "fmt"
import "gopkg.in/olebedev/go-duktape.v3"
func main() {
ctx := duktape.New()
ctx.PevalString(`2 + 3`)
result := ctx.GetNumber(-1)
ctx.Pop()
fmt.Println("result is:", result)
// To prevent memory leaks, don't forget to clean up after
// yourself when you're done using a context.
ctx.DestroyHeap()
}
```
### Go specific notes
Bindings between Go and Javascript contexts are not fully functional.
However, binding a Go function to the Javascript context is available:
```go
package main
import "fmt"
import "gopkg.in/olebedev/go-duktape.v3"
func main() {
ctx := duktape.New()
ctx.PushGlobalGoFunction("log", func(c *duktape.Context) int {
fmt.Println(c.SafeToString(-1))
return 0
})
ctx.PevalString(`log('Go lang Go!')`)
}
```
then run it.
```bash
$ go run *.go
Go lang Go!
$
```
### Timers
There is a method to inject timers to the global scope:
```go
package main
import "fmt"
import "gopkg.in/olebedev/go-duktape.v3"
func main() {
ctx := duktape.New()
// Let's inject `setTimeout`, `setInterval`, `clearTimeout`,
// `clearInterval` into global scope.
ctx.PushTimers()
ch := make(chan string)
ctx.PushGlobalGoFunction("second", func(_ *Context) int {
ch <- "second step"
return 0
})
ctx.PevalString(`
setTimeout(second, 0);
print('first step');
`)
fmt.Println(<-ch)
}
```
then run it
```bash
$ go run *.go
first step
second step
$
```
Also you can `FlushTimers()`.
### Command line tool
Install `go get gopkg.in/olebedev/go-duktape.v3/...`.
Execute file.js: `$GOPATH/bin/go-duk file.js`.
### Benchmarks
| prog | time |
| ------------|-------|
|[otto](https://github.com/robertkrimen/otto)|200.13s|
|[anko](https://github.com/mattn/anko)|231.19s|
|[agora](https://github.com/PuerkitoBio/agora/)|149.33s|
|[GopherLua](https://github.com/yuin/gopher-lua/)|8.39s|
|**go-duktape**|**9.80s**|
More details are [here](https://github.com/olebedev/go-duktape/wiki/Benchmarks).
### Status
The package is not fully tested, so be careful.
### Contribution
Pull requests are welcome! Also, if you want to discuss something send a pull request with proposal and changes.
__Convention:__ fork the repository and make changes on your fork in a feature branch.

1616
vendor/gopkg.in/olebedev/go-duktape.v3/api.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

34
vendor/gopkg.in/olebedev/go-duktape.v3/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,34 @@
os: Visual Studio 2015
clone_folder: C:\gopath\src\gopkg.in/olebedev/go-duktape.v3
clone_depth: 5
version: "{branch}.{build}"
environment:
global:
GOPATH: C:\gopath
CC: gcc.exe
matrix:
- DUKTAPE_ARCH: amd64
MSYS2_ARCH: x86_64
MSYS2_BITS: 64
MSYSTEM: MINGW64
PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH%
- DUKTAPE_ARCH: 386
MSYS2_ARCH: i686
MSYS2_BITS: 32
MSYSTEM: MINGW32
PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%
install:
- rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.9.2.windows-%DUKTAPE_ARCH%.zip
- 7z x go1.9.2.windows-%DUKTAPE_ARCH%.zip -y -oC:\ > NUL
- go version
- gcc --version
build_script:
- go get -t
- go install ./...
test_script:
- go test ./...

121
vendor/gopkg.in/olebedev/go-duktape.v3/conts.go generated vendored Normal file
View File

@@ -0,0 +1,121 @@
package duktape
const (
CompileEval uint = 1 << iota
CompileFunction
CompileStrict
CompileSafe
CompileNoResult
CompileNoSource
CompileStrlen
)
const (
TypeNone Type = iota
TypeUndefined
TypeNull
TypeBoolean
TypeNumber
TypeString
TypeObject
TypeBuffer
TypePointer
TypeLightFunc
)
const (
TypeMaskNone uint = 1 << iota
TypeMaskUndefined
TypeMaskNull
TypeMaskBoolean
TypeMaskNumber
TypeMaskString
TypeMaskObject
TypeMaskBuffer
TypeMaskPointer
TypeMaskLightFunc
)
const (
EnumIncludeNonenumerable uint = 1 << iota
EnumIncludeInternal
EnumOwnPropertiesOnly
EnumArrayIndicesOnly
EnumSortArrayIndices
NoProxyBehavior
)
const (
ErrNone int = 0
// Internal to Duktape
ErrUnimplemented int = 50 + iota
ErrUnsupported
ErrInternal
ErrAlloc
ErrAssertion
ErrAPI
ErrUncaughtError
)
const (
// Common prototypes
ErrError int = 1 + iota
ErrEval
ErrRange
ErrReference
ErrSyntax
ErrType
ErrURI
)
const (
// Returned error values
ErrRetUnimplemented int = -(ErrUnimplemented + iota)
ErrRetUnsupported
ErrRetInternal
ErrRetAlloc
ErrRetAssertion
ErrRetAPI
ErrRetUncaughtError
)
const (
ErrRetError int = -(ErrError + iota)
ErrRetEval
ErrRetRange
ErrRetReference
ErrRetSyntax
ErrRetType
ErrRetURI
)
const (
ExecSuccess = iota
ExecError
)
const (
LogTrace int = iota
LogDebug
LogInfo
LogWarn
LogError
LogFatal
)
const (
BufobjDuktapeAuffer = 0
BufobjNodejsAuffer = 1
BufobjArraybuffer = 2
BufobjDataview = 3
BufobjInt8array = 4
BufobjUint8array = 5
BufobjUint8clampedarray = 6
BufobjInt16array = 7
BufobjUint16array = 8
BufobjInt32array = 9
BufobjUint32array = 10
BufobjFloat32array = 11
BufobjFloat64array = 12
)

612
vendor/gopkg.in/olebedev/go-duktape.v3/duk_alloc_pool.c generated vendored Executable file
View File

@@ -0,0 +1,612 @@
/*
* Pool allocator for low memory targets.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include "duktape.h"
#include "duk_alloc_pool.h"
/* Define to enable some debug printfs. */
/* #define DUK_ALLOC_POOL_DEBUG */
/* Define to enable approximate waste tracking. */
/* #define DUK_ALLOC_POOL_TRACK_WASTE */
/* Define to track global highwater for used and waste bytes. VERY SLOW, only
* useful for manual testing.
*/
/* #define DUK_ALLOC_POOL_TRACK_HIGHWATER */
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
#if 0 /* This extern declaration is provided by duktape.h, array provided by duktape.c. */
extern const void * const duk_rom_compressed_pointers[];
#endif
const void *duk_alloc_pool_romptr_low = NULL;
const void *duk_alloc_pool_romptr_high = NULL;
static void duk__alloc_pool_romptr_init(void);
#endif
#if defined(DUK_USE_HEAPPTR16)
void *duk_alloc_pool_ptrcomp_base = NULL;
#endif
#if defined(DUK_ALLOC_POOL_DEBUG)
static void duk__alloc_pool_dprintf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#endif
/*
* Pool initialization
*/
void *duk_alloc_pool_init(char *buffer,
size_t size,
const duk_pool_config *configs,
duk_pool_state *states,
int num_pools,
duk_pool_global *global) {
double t_min, t_max, t_curr, x;
int step, i, j, n;
size_t total;
char *p;
/* XXX: check that 'size' is not too large when using pointer
* compression.
*/
/* To optimize pool counts first come up with a 't' which still allows
* total pool size to fit within user provided region. After that
* sprinkle any remaining bytes to the counts. Binary search with a
* fixed step count; last round uses 't_min' as 't_curr' to ensure it
* succeeds.
*/
t_min = 0.0; /* Unless config is insane, this should always be "good". */
t_max = 1e6;
for (step = 0; ; step++) {
if (step >= 100) {
/* Force "known good", rerun config, and break out.
* Deals with rounding corner cases where t_curr is
* persistently "bad" even though t_min is a valid
* solution.
*/
t_curr = t_min;
} else {
t_curr = (t_min + t_max) / 2.0;
}
for (i = 0, total = 0; i < num_pools; i++) {
states[i].size = configs[i].size;
/* Target bytes = A*t + B ==> target count = (A*t + B) / block_size.
* Rely on A and B being small enough so that 'x' won't wrap.
*/
x = ((double) configs[i].a * t_curr + (double) configs[i].b) / (double) configs[i].size;
states[i].count = (unsigned int) x;
total += (size_t) states[i].size * (size_t) states[i].count;
if (total > size) {
goto bad;
}
}
/* t_curr is good. */
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (good)\n",
step, t_min, t_curr, t_max, (long) total, (long) size);
#endif
if (step >= 100) {
/* Keep state[] initialization state. The state was
* created using the highest 't_min'.
*/
break;
}
t_min = t_curr;
continue;
bad:
/* t_curr is bad. */
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: step=%d, t=[%lf %lf %lf] -> total %ld/%ld (bad)\n",
step, t_min, t_curr, t_max, (long) total, (long) size);
#endif
if (step >= 1000) {
/* Cannot find any good solution; shouldn't happen
* unless config is bad or 'size' is so small that
* even a baseline allocation won't fit.
*/
return NULL;
}
t_max = t_curr;
/* continue */
}
/* The base configuration is now good; sprinkle any leftovers to
* pools in descending order. Note that for good t_curr, 'total'
* indicates allocated bytes so far and 'size - total' indicates
* leftovers.
*/
for (i = num_pools - 1; i >= 0; i--) {
while (size - total >= states[i].size) {
/* Ignore potential wrapping of states[i].count as the count
* is 32 bits and shouldn't wrap in practice.
*/
states[i].count++;
total += states[i].size;
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: sprinkle %ld bytes (%ld left after) to pool index %ld, new count %ld\n",
(long) states[i].size, (long) (size - total), (long) i, (long) states[i].count);
#endif
}
}
/* Pool counts are final. Allocate the user supplied region based
* on the final counts, initialize free lists for each block size,
* and otherwise finalize 'state' for use.
*/
p = buffer;
global->num_pools = num_pools;
global->states = states;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: global highwater mark tracking enabled, THIS IS VERY SLOW!\n");
#endif
global->hwm_used_bytes = 0U;
global->hwm_waste_bytes = 0U;
#endif
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: approximate waste tracking enabled\n");
#endif
#endif
#if defined(DUK_USE_HEAPPTR16)
/* Register global base value for pointer compression, assumes
* a single active pool -4 allows a single subtract to be used and
* still ensures no non-NULL pointer encodes to zero.
*/
duk_alloc_pool_ptrcomp_base = (void *) (p - 4);
#endif
for (i = 0; i < num_pools; i++) {
n = (int) states[i].count;
if (n > 0) {
states[i].first = (duk_pool_free *) p;
for (j = 0; j < n; j++) {
char *p_next = p + states[i].size;
((duk_pool_free *) p)->next =
(j == n - 1) ? (duk_pool_free *) NULL : (duk_pool_free *) p_next;
p = p_next;
}
} else {
states[i].first = (duk_pool_free *) NULL;
}
states[i].alloc_end = p;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
states[i].hwm_used_count = 0;
#endif
/* All members of 'state' now initialized. */
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool_init: block size %5ld, count %5ld, %8ld total bytes, "
"end %p\n",
(long) states[i].size, (long) states[i].count,
(long) states[i].size * (long) states[i].count,
(void *) states[i].alloc_end);
#endif
}
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
/* ROM pointer compression precomputation. Assumes a single active
* pool.
*/
duk__alloc_pool_romptr_init();
#endif
/* Use 'global' as udata. */
return (void *) global;
}
/*
* Misc helpers
*/
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) {
/* Rely on the base pointer and size being divisible by 4 and thus
* aligned. Use 32-bit markers: a 4-byte resolution is good enough,
* and comparing 32 bits at a time makes false waste estimates less
* likely than when comparing as bytes.
*/
duk_uint32_t *p, *p_start, *p_end;
size_t used_round;
used_round = (used + 3U) & ~0x03U; /* round up to 4 */
p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size);
p_start = (duk_uint32_t *) ((duk_uint8_t *) ptr + used_round);
p = (duk_uint32_t *) p_start;
while (p != p_end) {
*p++ = DUK_ALLOC_POOL_WASTE_MARKER;
}
}
#else /* DUK_ALLOC_POOL_TRACK_WASTE */
static void duk__alloc_pool_set_waste_marker(void *ptr, size_t used, size_t size) {
(void) ptr; (void) used; (void) size;
}
#endif /* DUK_ALLOC_POOL_TRACK_WASTE */
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) {
duk_uint32_t *p, *p_end, *p_start;
/* Assumes size is >= 4. */
p_start = (duk_uint32_t *) ptr;
p_end = (duk_uint32_t *) ((duk_uint8_t *) ptr + size);
p = p_end;
/* This scan may cause harmless valgrind complaints: there may be
* uninitialized bytes within the legitimate allocation or between
* the start of the waste marker and the end of the allocation.
*/
do {
p--;
if (*p == DUK_ALLOC_POOL_WASTE_MARKER) {
;
} else {
return (size_t) (p_end - p - 1) * 4U;
}
} while (p != p_start);
return size;
}
#else /* DUK_ALLOC_POOL_TRACK_WASTE */
static size_t duk__alloc_pool_get_waste_estimate(void *ptr, size_t size) {
(void) ptr; (void) size;
return 0;
}
#endif /* DUK_ALLOC_POOL_TRACK_WASTE */
static int duk__alloc_pool_ptr_in_freelist(duk_pool_state *s, void *ptr) {
duk_pool_free *curr;
for (curr = s->first; curr != NULL; curr = curr->next) {
if ((void *) curr == ptr) {
return 1;
}
}
return 0;
}
void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res) {
void *curr;
size_t free_count;
size_t used_count;
size_t waste_bytes;
curr = s->alloc_end - (s->size * s->count);
free_count = 0U;
waste_bytes = 0U;
while (curr != s->alloc_end) {
if (duk__alloc_pool_ptr_in_freelist(s, curr)) {
free_count++;
} else {
waste_bytes += duk__alloc_pool_get_waste_estimate(curr, s->size);
}
curr = curr + s->size;
}
used_count = (size_t) (s->count - free_count);
res->used_count = used_count;
res->used_bytes = (size_t) (used_count * s->size);
res->free_count = free_count;
res->free_bytes = (size_t) (free_count * s->size);
res->waste_bytes = waste_bytes;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
res->hwm_used_count = s->hwm_used_count;
#else
res->hwm_used_count = 0U;
#endif
}
void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res) {
int i;
size_t total_used = 0U;
size_t total_free = 0U;
size_t total_waste = 0U;
for (i = 0; i < g->num_pools; i++) {
duk_pool_state *s = &g->states[i];
duk_pool_stats stats;
duk_alloc_pool_get_pool_stats(s, &stats);
total_used += stats.used_bytes;
total_free += stats.free_bytes;
total_waste += stats.waste_bytes;
}
res->used_bytes = total_used;
res->free_bytes = total_free;
res->waste_bytes = total_waste;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
res->hwm_used_bytes = g->hwm_used_bytes;
res->hwm_waste_bytes = g->hwm_waste_bytes;
#else
res->hwm_used_bytes = 0U;
res->hwm_waste_bytes = 0U;
#endif
}
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
static void duk__alloc_pool_update_highwater(duk_pool_global *g) {
int i;
size_t total_used = 0U;
size_t total_free = 0U;
size_t total_waste = 0U;
/* Per pool highwater used count, useful to checking if a pool is
* too small.
*/
for (i = 0; i < g->num_pools; i++) {
duk_pool_state *s = &g->states[i];
duk_pool_stats stats;
duk_alloc_pool_get_pool_stats(s, &stats);
if (stats.used_count > s->hwm_used_count) {
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: pool %ld (%ld bytes) highwater updated: count %ld -> %ld\n",
(long) i, (long) s->size,
(long) s->hwm_used_count, (long) stats.used_count);
#endif
s->hwm_used_count = stats.used_count;
}
total_used += stats.used_bytes;
total_free += stats.free_bytes;
total_waste += stats.waste_bytes;
}
/* Global highwater mark for used and waste bytes. Both fields are
* updated from the same snapshot based on highest used count.
* This is VERY, VERY slow and only useful for development.
* (Note that updating HWM states for pools individually and then
* summing them won't create a consistent global snapshot. There
* are still easy ways to make this much, much faster.)
*/
if (total_used > g->hwm_used_bytes) {
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk__alloc_pool_update_highwater: global highwater updated: used=%ld, bytes=%ld -> "
"used=%ld, bytes=%ld\n",
(long) g->hwm_used_bytes, (long) g->hwm_waste_bytes,
(long) total_used, (long) total_waste);
#endif
g->hwm_used_bytes = total_used;
g->hwm_waste_bytes = total_waste;
}
}
#else /* DUK_ALLOC_POOL_TRACK_HIGHWATER */
static void duk__alloc_pool_update_highwater(duk_pool_global *g) {
(void) g;
}
#endif /* DUK_ALLOC_POOL_TRACK_HIGHWATER */
/*
* Allocation providers
*/
void *duk_alloc_pool(void *udata, duk_size_t size) {
duk_pool_global *g = (duk_pool_global *) udata;
int i, n;
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_alloc_pool: %p %ld\n", udata, (long) size);
#endif
if (size == 0) {
return NULL;
}
for (i = 0, n = g->num_pools; i < n; i++) {
duk_pool_state *st = g->states + i;
if (size <= st->size) {
duk_pool_free *res = st->first;
if (res != NULL) {
st->first = res->next;
duk__alloc_pool_set_waste_marker((void *) res, size, st->size);
duk__alloc_pool_update_highwater(g);
return (void *) res;
}
}
/* Allocation doesn't fit or no free entries, try to borrow
* from the next block size. There's no support for preventing
* a borrow at present.
*/
}
return NULL;
}
void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size) {
duk_pool_global *g = (duk_pool_global *) udata;
int i, j, n;
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_realloc_pool: %p %p %ld\n", udata, ptr, (long) size);
#endif
if (ptr == NULL) {
return duk_alloc_pool(udata, size);
}
if (size == 0) {
duk_free_pool(udata, ptr);
return NULL;
}
/* Non-NULL pointers are necessarily from the pool so we should
* always be able to find the allocation.
*/
for (i = 0, n = g->num_pools; i < n; i++) {
duk_pool_state *st = g->states + i;
char *new_ptr;
/* Because 'ptr' is assumed to be in the pool and pools are
* allocated in sequence, it suffices to check for end pointer
* only.
*/
if ((char *) ptr >= st->alloc_end) {
continue;
}
if (size <= st->size) {
/* Allocation still fits existing allocation. Check if
* we can shrink the allocation to a smaller block size
* (smallest possible).
*/
for (j = 0; j < i; j++) {
duk_pool_state *st2 = g->states + j;
if (size <= st2->size) {
new_ptr = (char *) st2->first;
if (new_ptr != NULL) {
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_realloc_pool: shrink, block size %ld -> %ld\n",
(long) st->size, (long) st2->size);
#endif
st2->first = ((duk_pool_free *) new_ptr)->next;
memcpy((void *) new_ptr, (const void *) ptr, (size_t) size);
((duk_pool_free *) ptr)->next = st->first;
st->first = (duk_pool_free *) ptr;
duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size);
duk__alloc_pool_update_highwater(g);
return (void *) new_ptr;
}
}
}
/* Failed to shrink; return existing pointer. */
duk__alloc_pool_set_waste_marker((void *) ptr, size, st->size);
return ptr;
}
/* Find first free larger block. */
for (j = i + 1; j < n; j++) {
duk_pool_state *st2 = g->states + j;
if (size <= st2->size) {
new_ptr = (char *) st2->first;
if (new_ptr != NULL) {
st2->first = ((duk_pool_free *) new_ptr)->next;
memcpy((void *) new_ptr, (const void *) ptr, (size_t) st->size);
((duk_pool_free *) ptr)->next = st->first;
st->first = (duk_pool_free *) ptr;
duk__alloc_pool_set_waste_marker((void *) new_ptr, size, st2->size);
duk__alloc_pool_update_highwater(g);
return (void *) new_ptr;
}
}
}
/* Failed to resize. */
return NULL;
}
/* We should never be here because 'ptr' should be a valid pool
* entry and thus always found above.
*/
return NULL;
}
void duk_free_pool(void *udata, void *ptr) {
duk_pool_global *g = (duk_pool_global *) udata;
int i, n;
#if defined(DUK_ALLOC_POOL_DEBUG)
duk__alloc_pool_dprintf("duk_free_pool: %p %p\n", udata, ptr);
#endif
if (ptr == NULL) {
return;
}
for (i = 0, n = g->num_pools; i < n; i++) {
duk_pool_state *st = g->states + i;
/* Enough to check end address only. */
if ((char *) ptr >= st->alloc_end) {
continue;
}
((duk_pool_free *) ptr)->next = st->first;
st->first = (duk_pool_free *) ptr;
#if 0 /* never necessary when freeing */
duk__alloc_pool_update_highwater(g);
#endif
return;
}
/* We should never be here because 'ptr' should be a valid pool
* entry and thus always found above.
*/
}
/*
* Pointer compression
*/
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
static void duk__alloc_pool_romptr_init(void) {
/* Scan ROM pointer range for faster detection of "is 'p' a ROM pointer"
* later on.
*/
const void * const * ptrs = (const void * const *) duk_rom_compressed_pointers;
duk_alloc_pool_romptr_low = duk_alloc_pool_romptr_high = (const void *) *ptrs;
while (*ptrs) {
if (*ptrs > duk_alloc_pool_romptr_high) {
duk_alloc_pool_romptr_high = (const void *) *ptrs;
}
if (*ptrs < duk_alloc_pool_romptr_low) {
duk_alloc_pool_romptr_low = (const void *) *ptrs;
}
ptrs++;
}
}
#endif
/* Encode/decode functions are defined in the header to allow inlining. */
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr) {
/* The if-condition should be the fastest possible check
* for "is 'ptr' in ROM?". If pointer is in ROM, we'd like
* to compress it quickly. Here we just scan a ~1K array
* which is very bad for performance.
*/
const void * const * ptrs = duk_rom_compressed_pointers;
while (*ptrs) {
if (*ptrs == ptr) {
return DUK_ALLOC_POOL_ROMPTR_FIRST + (duk_uint16_t) (ptrs - duk_rom_compressed_pointers);
}
ptrs++;
}
/* We should really never be here: Duktape should only be
* compressing pointers which are in the ROM compressed
* pointers list, which are known at 'make dist' time.
* We go on, causing a pointer compression error.
*/
return 0;
}
#endif

223
vendor/gopkg.in/olebedev/go-duktape.v3/duk_alloc_pool.h generated vendored Executable file
View File

@@ -0,0 +1,223 @@
#if !defined(DUK_ALLOC_POOL_H_INCLUDED)
#define DUK_ALLOC_POOL_H_INCLUDED
#include "duktape.h"
/* 32-bit (big endian) marker used at the end of pool entries so that wasted
* space can be detected. Waste tracking must be enabled explicitly.
*/
#if defined(DUK_ALLOC_POOL_TRACK_WASTE)
#define DUK_ALLOC_POOL_WASTE_MARKER 0xedcb2345UL
#endif
/* Pointer compression with ROM strings/objects:
*
* For now, use DUK_USE_ROM_OBJECTS to signal the need for compressed ROM
* pointers. DUK_USE_ROM_PTRCOMP_FIRST is provided for the ROM pointer
* compression range minimum to avoid duplication in user code.
*/
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
#define DUK_ALLOC_POOL_ROMPTR_COMPRESSION
#define DUK_ALLOC_POOL_ROMPTR_FIRST DUK_USE_ROM_PTRCOMP_FIRST
/* This extern declaration is provided by duktape.h, array provided by duktape.c.
* Because duk_config.h may include this file (to get the inline functions) we
* need to forward declare this also here.
*/
extern const void * const duk_rom_compressed_pointers[];
#endif
/* Pool configuration for a certain block size. */
typedef struct {
unsigned int size; /* must be divisible by 4 and >= sizeof(void *) */
unsigned int a; /* bytes (not count) to allocate: a*t + b, t is an arbitrary scale parameter */
unsigned int b;
} duk_pool_config;
/* Freelist entry, must fit into the smallest block size. */
struct duk_pool_free;
typedef struct duk_pool_free duk_pool_free;
struct duk_pool_free {
duk_pool_free *next;
};
/* Pool state for a certain block size. */
typedef struct {
duk_pool_free *first;
char *alloc_end;
unsigned int size;
unsigned int count;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
unsigned int hwm_used_count;
#endif
} duk_pool_state;
/* Statistics for a certain pool. */
typedef struct {
size_t used_count;
size_t used_bytes;
size_t free_count;
size_t free_bytes;
size_t waste_bytes;
size_t hwm_used_count;
} duk_pool_stats;
/* Top level state for all pools. Pointer to this struct is used as the allocator
* userdata pointer.
*/
typedef struct {
int num_pools;
duk_pool_state *states;
#if defined(DUK_ALLOC_POOL_TRACK_HIGHWATER)
size_t hwm_used_bytes;
size_t hwm_waste_bytes;
#endif
} duk_pool_global;
/* Statistics for the entire set of pools. */
typedef struct {
size_t used_bytes;
size_t free_bytes;
size_t waste_bytes;
size_t hwm_used_bytes;
size_t hwm_waste_bytes;
} duk_pool_global_stats;
/* Initialize a pool allocator, arguments:
* - buffer and size: continuous region to use for pool, must align to 4
* - config: configuration for pools in ascending block size
* - state: state for pools, matches config order
* - num_pools: number of entries in 'config' and 'state'
* - global: global state structure
*
* The 'config', 'state', and 'global' pointers must be valid beyond the init
* call, as long as the pool is used.
*
* Returns a void pointer to be used as userdata for the allocator functions.
* Concretely the return value will be "(void *) global", i.e. the global
* state struct. If pool init fails, the return value will be NULL.
*/
void *duk_alloc_pool_init(char *buffer,
size_t size,
const duk_pool_config *configs,
duk_pool_state *states,
int num_pools,
duk_pool_global *global);
/* Duktape allocation providers. Typing matches Duktape requirements. */
void *duk_alloc_pool(void *udata, duk_size_t size);
void *duk_realloc_pool(void *udata, void *ptr, duk_size_t size);
void duk_free_pool(void *udata, void *ptr);
/* Stats. */
void duk_alloc_pool_get_pool_stats(duk_pool_state *s, duk_pool_stats *res);
void duk_alloc_pool_get_global_stats(duk_pool_global *g, duk_pool_global_stats *res);
/* Duktape pointer compression global state (assumes single pool). */
#if defined(DUK_USE_ROM_OBJECTS) && defined(DUK_USE_HEAPPTR16)
extern const void *duk_alloc_pool_romptr_low;
extern const void *duk_alloc_pool_romptr_high;
duk_uint16_t duk_alloc_pool_enc16_rom(void *ptr);
#endif
#if defined(DUK_USE_HEAPPTR16)
extern void *duk_alloc_pool_ptrcomp_base;
#endif
#if 0
duk_uint16_t duk_alloc_pool_enc16(void *ptr);
void *duk_alloc_pool_dec16(duk_uint16_t val);
#endif
/* Inlined pointer compression functions. Gcc and clang -Os won't in
* practice inline these without an "always inline" attribute because it's
* more size efficient (by a few kB) to use explicit calls instead. Having
* these defined inline here allows performance optimized builds to inline
* pointer compression operations.
*
* Pointer compression assumes there's a single globally registered memory
* pool which makes pointer compression more efficient. This would be easy
* to fix by adding a userdata pointer to the compression functions and
* plumbing the heap userdata from the compression/decompression macros.
*/
/* DUK_ALWAYS_INLINE is not a public API symbol so it may go away in even a
* minor update. But it's pragmatic for this extra because it handles many
* compilers via duk_config.h detection. Check that the macro exists so that
* if it's gone, we can still compile.
*/
#if defined(DUK_ALWAYS_INLINE)
#define DUK__ALLOC_POOL_ALWAYS_INLINE DUK_ALWAYS_INLINE
#else
#define DUK__ALLOC_POOL_ALWAYS_INLINE /* nop */
#endif
#if defined(DUK_USE_HEAPPTR16)
static DUK__ALLOC_POOL_ALWAYS_INLINE duk_uint16_t duk_alloc_pool_enc16(void *ptr) {
if (ptr == NULL) {
/* With 'return 0' gcc and clang -Os generate inefficient code.
* For example, gcc -Os generates:
*
* 0804911d <duk_alloc_pool_enc16>:
* 804911d: 55 push %ebp
* 804911e: 85 c0 test %eax,%eax
* 8049120: 89 e5 mov %esp,%ebp
* 8049122: 74 0b je 804912f <duk_alloc_pool_enc16+0x12>
* 8049124: 2b 05 e4 90 07 08 sub 0x80790e4,%eax
* 804912a: c1 e8 02 shr $0x2,%eax
* 804912d: eb 02 jmp 8049131 <duk_alloc_pool_enc16+0x14>
* 804912f: 31 c0 xor %eax,%eax
* 8049131: 5d pop %ebp
* 8049132: c3 ret
*
* The NULL path checks %eax for zero; if it is zero, a zero
* is unnecessarily loaded into %eax again. The non-zero path
* has an unnecessary jump as a side effect of this.
*
* Using 'return (duk_uint16_t) (intptr_t) ptr;' generates similarly
* inefficient code; not sure how to make the result better.
*/
return 0;
}
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
if (ptr >= duk_alloc_pool_romptr_low && ptr <= duk_alloc_pool_romptr_high) {
/* This is complex enough now to need a separate function. */
return duk_alloc_pool_enc16_rom(ptr);
}
#endif
return (duk_uint16_t) (((size_t) ((char *) ptr - (char *) duk_alloc_pool_ptrcomp_base)) >> 2);
}
static DUK__ALLOC_POOL_ALWAYS_INLINE void *duk_alloc_pool_dec16(duk_uint16_t val) {
if (val == 0) {
/* As with enc16 the gcc and clang -Os output is inefficient,
* e.g. gcc -Os:
*
* 08049133 <duk_alloc_pool_dec16>:
* 8049133: 55 push %ebp
* 8049134: 66 85 c0 test %ax,%ax
* 8049137: 89 e5 mov %esp,%ebp
* 8049139: 74 0e je 8049149 <duk_alloc_pool_dec16+0x16>
* 804913b: 8b 15 e4 90 07 08 mov 0x80790e4,%edx
* 8049141: 0f b7 c0 movzwl %ax,%eax
* 8049144: 8d 04 82 lea (%edx,%eax,4),%eax
* 8049147: eb 02 jmp 804914b <duk_alloc_pool_dec16+0x18>
* 8049149: 31 c0 xor %eax,%eax
* 804914b: 5d pop %ebp
* 804914c: c3 ret
*/
return NULL;
}
#if defined(DUK_ALLOC_POOL_ROMPTR_COMPRESSION)
if (val >= DUK_ALLOC_POOL_ROMPTR_FIRST) {
/* This is a blind lookup, could check index validity.
* Duktape should never decompress a pointer which would
* be out-of-bounds here.
*/
return (void *) (intptr_t) (duk_rom_compressed_pointers[val - DUK_ALLOC_POOL_ROMPTR_FIRST]);
}
#endif
return (void *) ((char *) duk_alloc_pool_ptrcomp_base + (((size_t) val) << 2));
}
#endif
#endif /* DUK_ALLOC_POOL_H_INCLUDED */

3672
vendor/gopkg.in/olebedev/go-duktape.v3/duk_config.h generated vendored Executable file

File diff suppressed because it is too large Load Diff

163
vendor/gopkg.in/olebedev/go-duktape.v3/duk_console.c generated vendored Executable file
View File

@@ -0,0 +1,163 @@
/*
* Minimal 'console' binding.
*
* https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
* https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
* https://developer.mozilla.org/en/docs/Web/API/console
*/
#include <stdio.h>
#include <stdarg.h>
#include "duktape.h"
#include "duk_console.h"
/* XXX: Add some form of log level filtering. */
/* XXX: For now logs everything to stdout, V8/Node.js logs debug/info level
* to stdout, warn and above to stderr. Should this extra do the same?
*/
/* XXX: Should all output be written via e.g. console.write(formattedMsg)?
* This would make it easier for user code to redirect all console output
* to a custom backend.
*/
/* XXX: Init console object using duk_def_prop() when that call is available. */
static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
duk_idx_t i, n;
duk_uint_t flags;
flags = (duk_uint_t) duk_get_current_magic(ctx);
n = duk_get_top(ctx);
duk_get_global_string(ctx, "console");
duk_get_prop_string(ctx, -1, "format");
for (i = 0; i < n; i++) {
if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
/* Slow path formatting. */
duk_dup(ctx, -1); /* console.format */
duk_dup(ctx, i);
duk_call(ctx, 1);
duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
}
}
duk_pop_2(ctx);
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_join(ctx, n);
if (error_name) {
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
duk_push_string(ctx, "name");
duk_push_string(ctx, error_name);
duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
duk_get_prop_string(ctx, -1, "stack");
}
fprintf(stdout, "%s\n", duk_to_string(ctx, -1));
if (flags & DUK_CONSOLE_FLUSH) {
fflush(stdout);
}
return 0;
}
static duk_ret_t duk__console_assert(duk_context *ctx) {
if (duk_to_boolean(ctx, 0)) {
return 0;
}
duk_remove(ctx, 0);
return duk__console_log_helper(ctx, "AssertionError");
}
static duk_ret_t duk__console_log(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_trace(duk_context *ctx) {
return duk__console_log_helper(ctx, "Trace");
}
static duk_ret_t duk__console_info(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_warn(duk_context *ctx) {
return duk__console_log_helper(ctx, NULL);
}
static duk_ret_t duk__console_error(duk_context *ctx) {
return duk__console_log_helper(ctx, "Error");
}
static duk_ret_t duk__console_dir(duk_context *ctx) {
/* For now, just share the formatting of .log() */
return duk__console_log_helper(ctx, 0);
}
static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
duk_push_c_function(ctx, func, DUK_VARARGS);
duk_push_string(ctx, "name");
duk_push_string(ctx, name);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
duk_set_magic(ctx, -1, (duk_int_t) flags);
duk_put_prop_string(ctx, -2, name);
}
void duk_console_init(duk_context *ctx, duk_uint_t flags) {
duk_push_object(ctx);
/* Custom function to format objects; user can replace.
* For now, try JX-formatting and if that fails, fall back
* to ToString(v).
*/
duk_eval_string(ctx,
"(function (E) {"
"return function format(v){"
"try{"
"return E('jx',v);"
"}catch(e){"
"return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
"}"
"};"
"})(Duktape.enc)");
duk_put_prop_string(ctx, -2, "format");
duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
duk_put_global_string(ctx, "console");
/* Proxy wrapping: ensures any undefined console method calls are
* ignored silently. This is required specifically by the
* DeveloperToolsWG proposal (and is implemented also by Firefox:
* https://bugzilla.mozilla.org/show_bug.cgi?id=629607).
*/
if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
/* Tolerate errors: Proxy may be disabled. */
duk_peval_string_noresult(ctx,
"(function(){"
"var D=function(){};"
"console=new Proxy(console,{"
"get:function(t,k){"
"var v=t[k];"
"return typeof v==='function'?v:D;"
"}"
"});"
"})();"
);
}
}

14
vendor/gopkg.in/olebedev/go-duktape.v3/duk_console.h generated vendored Executable file
View File

@@ -0,0 +1,14 @@
#if !defined(DUK_CONSOLE_H_INCLUDED)
#define DUK_CONSOLE_H_INCLUDED
#include "duktape.h"
/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */
#define DUK_CONSOLE_PROXY_WRAPPER (1 << 0)
/* Flush output after every call. */
#define DUK_CONSOLE_FLUSH (1 << 1)
extern void duk_console_init(duk_context *ctx, duk_uint_t flags);
#endif /* DUK_CONSOLE_H_INCLUDED */

380
vendor/gopkg.in/olebedev/go-duktape.v3/duk_logging.c generated vendored Executable file
View File

@@ -0,0 +1,380 @@
/*
* Logging support
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "duktape.h"
#include "duk_logging.h"
/* XXX: uses stderr always for now, configurable? */
#define DUK_LOGGING_FLUSH /* Duktape 1.x: flush stderr */
/* 3-letter log level strings. */
static const char duk__log_level_strings[] = {
'T', 'R', 'C', 'D', 'B', 'G', 'I', 'N', 'F',
'W', 'R', 'N', 'E', 'R', 'R', 'F', 'T', 'L'
};
/* Log method names. */
static const char *duk__log_method_names[] = {
"trace", "debug", "info", "warn", "error", "fatal"
};
/* Constructor. */
static duk_ret_t duk__logger_constructor(duk_context *ctx) {
duk_idx_t nargs;
/* Calling as a non-constructor is not meaningful. */
if (!duk_is_constructor_call(ctx)) {
return DUK_RET_TYPE_ERROR;
}
nargs = duk_get_top(ctx);
duk_set_top(ctx, 1);
duk_push_this(ctx);
/* [ name this ] */
if (nargs == 0) {
/* Automatic defaulting of logger name from caller. This
* would work poorly with tail calls, but constructor calls
* are currently never tail calls, so tail calls are not an
* issue now.
*/
duk_inspect_callstack_entry(ctx, -2);
if (duk_is_object(ctx, -1)) {
if (duk_get_prop_string(ctx, -1, "function")) {
if (duk_get_prop_string(ctx, -1, "fileName")) {
if (duk_is_string(ctx, -1)) {
duk_replace(ctx, 0);
}
}
}
}
/* Leave values on stack on purpose, ignored below. */
/* Stripping the filename might be a good idea
* ("/foo/bar/quux.js" -> logger name "quux"),
* but now used verbatim.
*/
}
/* The stack is unbalanced here on purpose; we only rely on the
* initial two values: [ name this ].
*/
if (duk_is_string(ctx, 0)) {
duk_dup(ctx, 0);
duk_put_prop_string(ctx, 1, "n");
} else {
/* don't set 'n' at all, inherited value is used as name */
}
duk_compact(ctx, 1);
return 0; /* keep default instance */
}
/* Default function to format objects. Tries to use toLogString() but falls
* back to toString(). Any errors are propagated out without catching.
*/
static duk_ret_t duk__logger_prototype_fmt(duk_context *ctx) {
if (duk_get_prop_string(ctx, 0, "toLogString")) {
/* [ arg toLogString ] */
duk_dup(ctx, 0);
duk_call_method(ctx, 0);
/* [ arg result ] */
return 1;
}
/* [ arg undefined ] */
duk_pop(ctx);
duk_to_string(ctx, 0);
return 1;
}
/* Default function to write a formatted log line. Writes to stderr,
* appending a newline to the log line.
*
* The argument is a buffer; avoid coercing the buffer to a string to
* avoid string table traffic.
*/
static duk_ret_t duk__logger_prototype_raw(duk_context *ctx) {
const char *data;
duk_size_t data_len;
data = (const char *) duk_require_buffer(ctx, 0, &data_len);
fwrite((const void *) data, 1, data_len, stderr);
fputc((int) '\n', stderr);
#if defined(DUK_LOGGING_FLUSH)
fflush(stderr);
#endif
return 0;
}
/* Log frontend shared helper, magic value indicates log level. Provides
* frontend functions: trace(), debug(), info(), warn(), error(), fatal().
* This needs to have small footprint, reasonable performance, minimal
* memory churn, etc.
*/
static duk_ret_t duk__logger_prototype_log_shared(duk_context *ctx) {
duk_double_t now;
duk_time_components comp;
duk_small_int_t entry_lev;
duk_small_int_t logger_lev;
duk_int_t nargs;
duk_int_t i;
duk_size_t tot_len;
const duk_uint8_t *arg_str;
duk_size_t arg_len;
duk_uint8_t *buf, *p;
const duk_uint8_t *q;
duk_uint8_t date_buf[32]; /* maximum format length is 24+1 (NUL), round up. */
duk_size_t date_len;
duk_small_int_t rc;
/* XXX: sanitize to printable (and maybe ASCII) */
/* XXX: better multiline */
/*
* Logger arguments are:
*
* magic: log level (0-5)
* this: logger
* stack: plain log args
*
* We want to minimize memory churn so a two-pass approach
* is used: first pass formats arguments and computes final
* string length, second pass copies strings into a buffer
* allocated directly with the correct size. If the backend
* function plays nice, it won't coerce the buffer to a string
* (and thus intern it).
*/
entry_lev = duk_get_current_magic(ctx);
if (entry_lev < DUK_LOG_TRACE || entry_lev > DUK_LOG_FATAL) {
/* Should never happen, check just in case. */
return 0;
}
nargs = duk_get_top(ctx);
/* [ arg1 ... argN this ] */
/*
* Log level check
*/
duk_push_this(ctx);
duk_get_prop_string(ctx, -1, "l");
logger_lev = (duk_small_int_t) duk_get_int(ctx, -1);
if (entry_lev < logger_lev) {
return 0;
}
/* log level could be popped but that's not necessary */
now = duk_get_now(ctx);
duk_time_to_components(ctx, now, &comp);
sprintf((char *) date_buf, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
(int) comp.year, (int) comp.month + 1, (int) comp.day,
(int) comp.hours, (int) comp.minutes, (int) comp.seconds,
(int) comp.milliseconds);
date_len = strlen((const char *) date_buf);
duk_get_prop_string(ctx, -2, "n");
duk_to_string(ctx, -1);
/* [ arg1 ... argN this loggerLevel loggerName ] */
/*
* Pass 1
*/
/* Line format: <time> <entryLev> <loggerName>: <msg> */
tot_len = 0;
tot_len += 3 + /* separators: space, space, colon */
3 + /* level string */
date_len + /* time */
duk_get_length(ctx, -1); /* loggerName */
for (i = 0; i < nargs; i++) {
/* When formatting an argument to a string, errors may happen from multiple
* causes. In general we want to catch obvious errors like a toLogString()
* throwing an error, but we don't currently try to catch every possible
* error. In particular, internal errors (like out of memory or stack) are
* not caught. Also, we expect Error toString() to not throw an error.
*/
if (duk_is_object(ctx, i)) {
/* duk_pcall_prop() may itself throw an error, but we're content
* in catching the obvious errors (like toLogString() throwing an
* error).
*/
duk_push_string(ctx, "fmt");
duk_dup(ctx, i);
/* [ arg1 ... argN this loggerLevel loggerName 'fmt' arg ] */
/* call: this.fmt(arg) */
rc = duk_pcall_prop(ctx, -5 /*obj_index*/, 1 /*nargs*/);
if (rc) {
/* Keep the error as the result (coercing it might fail below,
* but we don't catch that now).
*/
;
}
duk_replace(ctx, i);
}
(void) duk_to_lstring(ctx, i, &arg_len);
tot_len++; /* sep (even before first one) */
tot_len += arg_len;
}
/*
* Pass 2
*/
/* XXX: Here it'd be nice if we didn't need to allocate a new fixed
* buffer for every write. This would be possible if raw() took a
* buffer and a length. We could then use a preallocated buffer for
* most log writes and request raw() to write a partial buffer.
*/
buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, tot_len);
p = buf;
memcpy((void *) p, (const void *) date_buf, (size_t) date_len);
p += date_len;
*p++ = (duk_uint8_t) ' ';
q = (const duk_uint8_t *) duk__log_level_strings + (entry_lev * 3);
memcpy((void *) p, (const void *) q, (size_t) 3);
p += 3;
*p++ = (duk_uint8_t) ' ';
arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, -2, &arg_len);
memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
p += arg_len;
*p++ = (duk_uint8_t) ':';
for (i = 0; i < nargs; i++) {
*p++ = (duk_uint8_t) ' ';
arg_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &arg_len);
memcpy((void *) p, (const void *) arg_str, (size_t) arg_len);
p += arg_len;
}
/* [ arg1 ... argN this loggerLevel loggerName buffer ] */
/* Call this.raw(msg); look up through the instance allows user to override
* the raw() function in the instance or in the prototype for maximum
* flexibility.
*/
duk_push_string(ctx, "raw");
duk_dup(ctx, -2);
/* [ arg1 ... argN this loggerLevel loggerName buffer 'raw' buffer ] */
duk_call_prop(ctx, -6, 1); /* this.raw(buffer) */
return 0;
}
void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap) {
if (level < 0) {
level = 0;
} else if (level > (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1) {
level = (int) (sizeof(duk__log_method_names) / sizeof(const char *)) - 1;
}
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, "\xff" "logger:constructor"); /* fixed at init time */
duk_get_prop_string(ctx, -1, "clog");
duk_get_prop_string(ctx, -1, duk__log_method_names[level]);
duk_dup(ctx, -2);
duk_push_vsprintf(ctx, fmt, ap);
/* [ ... stash Logger clog logfunc clog(=this) msg ] */
duk_call_method(ctx, 1 /*nargs*/);
/* [ ... stash Logger clog res ] */
duk_pop_n(ctx, 4);
}
void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
duk_log_va(ctx, level, fmt, ap);
va_end(ap);
}
void duk_logging_init(duk_context *ctx, duk_uint_t flags) {
/* XXX: Add .name property for logger functions (useful for stack traces if they throw). */
(void) flags;
duk_eval_string(ctx,
"(function(cons,prot){"
"Object.defineProperty(Duktape,'Logger',{value:cons,writable:true,configurable:true});"
"Object.defineProperty(cons,'prototype',{value:prot});"
"Object.defineProperty(cons,'clog',{value:new Duktape.Logger('C'),writable:true,configurable:true});"
"});");
duk_push_c_function(ctx, duk__logger_constructor, DUK_VARARGS /*nargs*/); /* Duktape.Logger */
duk_push_object(ctx); /* Duktape.Logger.prototype */
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
duk_push_string(ctx, "name");
duk_push_string(ctx, "Logger");
duk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
duk_dup_top(ctx);
duk_put_prop_string(ctx, -2, "constructor");
duk_push_int(ctx, 2);
duk_put_prop_string(ctx, -2, "l");
duk_push_string(ctx, "anon");
duk_put_prop_string(ctx, -2, "n");
duk_push_c_function(ctx, duk__logger_prototype_fmt, 1 /*nargs*/);
duk_put_prop_string(ctx, -2, "fmt");
duk_push_c_function(ctx, duk__logger_prototype_raw, 1 /*nargs*/);
duk_put_prop_string(ctx, -2, "raw");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 0); /* magic=0: trace */
duk_put_prop_string(ctx, -2, "trace");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 1); /* magic=1: debug */
duk_put_prop_string(ctx, -2, "debug");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 2); /* magic=2: info */
duk_put_prop_string(ctx, -2, "info");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 3); /* magic=3: warn */
duk_put_prop_string(ctx, -2, "warn");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 4); /* magic=4: error */
duk_put_prop_string(ctx, -2, "error");
duk_push_c_function(ctx, duk__logger_prototype_log_shared, DUK_VARARGS /*nargs*/);
duk_set_magic(ctx, -1, 5); /* magic=5: fatal */
duk_put_prop_string(ctx, -2, "fatal");
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
/* XXX: when using ROM built-ins, "Duktape" is read-only by default so
* setting Duktape.Logger will now fail.
*/
/* [ ... func Duktape.Logger Duktape.Logger.prototype ] */
duk_call(ctx, 2);
duk_pop(ctx);
}

20
vendor/gopkg.in/olebedev/go-duktape.v3/duk_logging.h generated vendored Executable file
View File

@@ -0,0 +1,20 @@
#if !defined(DUK_LOGGING_H_INCLUDED)
#define DUK_LOGGING_H_INCLUDED
#include "duktape.h"
/* Log levels */
#define DUK_LOG_TRACE 0
#define DUK_LOG_DEBUG 1
#define DUK_LOG_INFO 2
#define DUK_LOG_WARN 3
#define DUK_LOG_ERROR 4
#define DUK_LOG_FATAL 5
/* No flags at the moment. */
extern void duk_logging_init(duk_context *ctx, duk_uint_t flags);
extern void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap);
extern void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...);
#endif /* DUK_LOGGING_H_INCLUDED */

312
vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c generated vendored Executable file
View File

@@ -0,0 +1,312 @@
/*
* Minimal vsnprintf(), snprintf(), sprintf(), and sscanf() for Duktape.
* The supported conversion formats narrowly match what Duktape needs.
*/
#include <stdarg.h> /* va_list etc */
#include <stddef.h> /* size_t */
#include <stdint.h> /* SIZE_MAX */
/* Write character with bound checking. Offset 'off' is updated regardless
* of whether an actual write is made. This is necessary to satisfy snprintf()
* return value semantics.
*/
#define DUK__WRITE_CHAR(c) do { \
if (off < size) { \
str[off] = (char) c; \
} \
off++; \
} while (0)
/* Digits up to radix 16. */
static const char duk__format_digits[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/* Format an unsigned long with various options. An unsigned long is large
* enough for formatting all supported types.
*/
static size_t duk__format_long(char *str,
size_t size,
size_t off,
int fixed_length,
char pad,
int radix,
int neg_sign,
unsigned long v) {
char buf[24]; /* 2^64 = 18446744073709552000, length 20 */
char *required;
char *p;
int i;
/* Format in reverse order first. Ensure at least one digit is output
* to handle '0' correctly. Note that space padding and zero padding
* handle negative sign differently:
*
* %9d and -321 => ' -321'
* %09d and -321 => '-00000321'
*/
for (i = 0; i < (int) sizeof(buf); i++) {
buf[i] = pad; /* compiles into memset() equivalent, avoid memset() dependency */
}
p = buf;
do {
*p++ = duk__format_digits[v % radix];
v /= radix;
} while (v != 0);
required = buf + fixed_length;
if (p < required && pad == (char) '0') {
/* Zero padding and we didn't reach maximum length: place
* negative sign at the last position. We can't get here
* with fixed_length == 0 so that required[-1] is safe.
*
* Technically we should only do this for 'neg_sign == 1',
* but it's OK to advance the pointer even when that's not
* the case.
*/
p = required - 1;
}
if (neg_sign) {
*p++ = (char) '-';
}
if (p < required) {
p = required;
}
/* Now [buf,p[ contains the result in reverse; copy into place. */
while (p > buf) {
p--;
DUK__WRITE_CHAR(*p);
}
return off;
}
/* Parse a pointer. Must parse whatever is produced by '%p' in sprintf(). */
static int duk__parse_pointer(const char *str, void **out) {
const unsigned char *p;
unsigned char ch;
int count;
int limit;
long val; /* assume void * fits into long */
/* We only need to parse what our minimal printf() produces, so that
* we can check for a '0x' prefix, and assume all hex digits are
* lowercase.
*/
p = (const unsigned char *) str;
if (p[0] != (unsigned char) '0' || p[1] != (unsigned char) 'x') {
return 0;
}
p += 2;
for (val = 0, count = 0, limit = sizeof(void *) * 2; count < limit; count++) {
ch = *p++;
val <<= 4;
if (ch >= (unsigned char) '0' && ch <= (unsigned char) '9') {
val += ch - (unsigned char) '0';
} else if (ch >= (unsigned char) 'a' && ch <= (unsigned char) 'f') {
val += ch - (unsigned char) 'a' + 0x0a;
} else {
return 0;
}
}
/* The input may end at a NUL or garbage may follow. As long as we
* parse the '%p' correctly, garbage is allowed to follow, and the
* JX pointer parsing also relies on that.
*/
*out = (void *) val;
return 1;
}
/* Minimal vsnprintf() entry point. */
int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
size_t off = 0;
const char *p;
#if 0
const char *p_tmp;
const char *p_fmt_start;
#endif
char c;
char pad;
int fixed_length;
int is_long;
/* Assume str != NULL unless size == 0.
* Assume format != NULL.
*/
p = format;
for (;;) {
c = *p++;
if (c == (char) 0) {
break;
}
if (c != (char) '%') {
DUK__WRITE_CHAR(c);
continue;
}
/* Start format sequence. Scan flags and format specifier. */
#if 0
p_fmt_start = p - 1;
#endif
is_long = 0;
pad = ' ';
fixed_length = 0;
for (;;) {
c = *p++;
if (c == (char) 'l') {
is_long = 1;
} else if (c == (char) '0') {
/* Only support pad character '0'. */
pad = '0';
} else if (c >= (char) '1' && c <= (char) '9') {
/* Only support fixed lengths 1-9. */
fixed_length = (int) (c - (char) '0');
} else if (c == (char) 'd') {
long v;
int neg_sign = 0;
if (is_long) {
v = va_arg(ap, long);
} else {
v = (long) va_arg(ap, int);
}
if (v < 0) {
neg_sign = 1;
v = -v;
}
off = duk__format_long(str, size, off, fixed_length, pad, 10, neg_sign, (unsigned long) v);
break;
} else if (c == (char) 'u') {
unsigned long v;
if (is_long) {
v = va_arg(ap, unsigned long);
} else {
v = (unsigned long) va_arg(ap, unsigned int);
}
off = duk__format_long(str, size, off, fixed_length, pad, 10, 0, v);
break;
} else if (c == (char) 'x') {
unsigned long v;
if (is_long) {
v = va_arg(ap, unsigned long);
} else {
v = (unsigned long) va_arg(ap, unsigned int);
}
off = duk__format_long(str, size, off, fixed_length, pad, 16, 0, v);
break;
} else if (c == (char) 'c') {
char v;
v = (char) va_arg(ap, int); /* intentionally not 'char' */
DUK__WRITE_CHAR(v);
break;
} else if (c == (char) 's') {
const char *v;
char c_tmp;
v = va_arg(ap, const char *);
if (v) {
for (;;) {
c_tmp = *v++;
if (c_tmp) {
DUK__WRITE_CHAR(c_tmp);
} else {
break;
}
}
}
break;
} else if (c == (char) 'p') {
/* Assume a void * can be represented by 'long'. This is not
* always the case. NULL pointer is printed out as 0x0000...
*/
void *v;
v = va_arg(ap, void *);
DUK__WRITE_CHAR('0');
DUK__WRITE_CHAR('x');
off = duk__format_long(str, size, off, sizeof(void *) * 2, '0', 16, 0, (unsigned long) v);
break;
} else {
/* Unrecognized, bail out early. We could also emit the format
* specifier verbatim, but it'd be a waste of footprint because
* this case should never happen in practice.
*/
#if 0
DUK__WRITE_CHAR('!');
#endif
#if 0
for (p_tmp = p_fmt_start; p_tmp != p; p_tmp++) {
DUK__WRITE_CHAR(*p_tmp);
}
break;
#endif
goto finish;
}
}
}
finish:
if (off < size) {
str[off] = (char) 0; /* No increment for 'off', not counted in return value. */
} else if (size > 0) {
/* Forced termination. */
str[size - 1] = 0;
}
return (int) off;
}
/* Minimal snprintf() entry point. */
int duk_minimal_snprintf(char *str, size_t size, const char *format, ...) {
va_list ap;
int ret;
va_start(ap, format);
ret = duk_minimal_vsnprintf(str, size, format, ap);
va_end(ap);
return ret;
}
/* Minimal sprintf() entry point. */
int duk_minimal_sprintf(char *str, const char *format, ...) {
va_list ap;
int ret;
va_start(ap, format);
ret = duk_minimal_vsnprintf(str, SIZE_MAX, format, ap);
va_end(ap);
return ret;
}
/* Minimal sscanf() entry point. */
int duk_minimal_sscanf(const char *str, const char *format, ...) {
va_list ap;
int ret;
void **out;
/* Only the exact "%p" format is supported. */
if (format[0] != (char) '%' ||
format[1] != (char) 'p' ||
format[2] != (char) 0) {
}
va_start(ap, format);
out = va_arg(ap, void **);
ret = duk__parse_pointer(str, out);
va_end(ap);
return ret;
}
#undef DUK__WRITE_CHAR

12
vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.h generated vendored Executable file
View File

@@ -0,0 +1,12 @@
#if !defined(DUK_MINIMAL_PRINTF_H_INCLUDED)
#define DUK_MINIMAL_PRINTF_H_INCLUDED
#include <stdarg.h> /* va_list etc */
#include <stddef.h> /* size_t */
extern int duk_minimal_sprintf(char *str, const char *format, ...);
extern int duk_minimal_snprintf(char *str, size_t size, const char *format, ...);
extern int duk_minimal_vsnprintf(char *str, size_t size, const char *format, va_list ap);
extern int duk_minimal_sscanf(const char *str, const char *format, ...);
#endif /* DUK_MINIMAL_PRINTF_H_INCLUDED */

471
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_duktape.c generated vendored Executable file
View File

@@ -0,0 +1,471 @@
/*
* Duktape 1.x compatible module loading framework
*/
#include "duktape.h"
#include "duk_module_duktape.h"
/* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does
* NOT NUL terminate on truncation, but that's OK here.
* http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
*/
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define snprintf _snprintf
#endif
#if 0 /* Enable manually */
#define DUK__ASSERT(x) do { \
if (!(x)) { \
fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \
fflush(stderr); \
} \
} while (0)
#define DUK__ASSERT_TOP(ctx,val) do { \
DUK__ASSERT(duk_get_top((ctx)) == (val)); \
} while (0)
#else
#define DUK__ASSERT(x) do { (void) (x); } while (0)
#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0)
#endif
static void duk__resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) {
duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT];
duk_uint8_t *p;
duk_uint8_t *q;
duk_uint8_t *q_last; /* last component */
duk_int_t int_rc;
DUK__ASSERT(req_id != NULL);
/* mod_id may be NULL */
/*
* A few notes on the algorithm:
*
* - Terms are not allowed to begin with a period unless the term
* is either '.' or '..'. This simplifies implementation (and
* is within CommonJS modules specification).
*
* - There are few output bound checks here. This is on purpose:
* the resolution input is length checked and the output is never
* longer than the input. The resolved output is written directly
* over the input because it's never longer than the input at any
* point in the algorithm.
*
* - Non-ASCII characters are processed as individual bytes and
* need no special treatment. However, U+0000 terminates the
* algorithm; this is not an issue because U+0000 is not a
* desirable term character anyway.
*/
/*
* Set up the resolution input which is the requested ID directly
* (if absolute or no current module path) or with current module
* ID prepended (if relative and current module path exists).
*
* Suppose current module is 'foo/bar' and relative path is './quux'.
* The 'bar' component must be replaced so the initial input here is
* 'foo/bar/.././quux'.
*/
if (mod_id != NULL && req_id[0] == '.') {
int_rc = snprintf((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id);
} else {
int_rc = snprintf((char *) buf, sizeof(buf), "%s", req_id);
}
if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) {
/* Potentially truncated, NUL not guaranteed in any case.
* The (int_rc < 0) case should not occur in practice.
*/
goto resolve_error;
}
DUK__ASSERT(strlen((const char *) buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */
/*
* Resolution loop. At the top of the loop we're expecting a valid
* term: '.', '..', or a non-empty identifier not starting with a period.
*/
p = buf;
q = buf;
for (;;) {
duk_uint_fast8_t c;
/* Here 'p' always points to the start of a term.
*
* We can also unconditionally reset q_last here: if this is
* the last (non-empty) term q_last will have the right value
* on loop exit.
*/
DUK__ASSERT(p >= q); /* output is never longer than input during resolution */
q_last = q;
c = *p++;
if (c == 0) {
goto resolve_error;
} else if (c == '.') {
c = *p++;
if (c == '/') {
/* Term was '.' and is eaten entirely (including dup slashes). */
goto eat_dup_slashes;
}
if (c == '.' && *p == '/') {
/* Term was '..', backtrack resolved name by one component.
* q[-1] = previous slash (or beyond start of buffer)
* q[-2] = last char of previous component (or beyond start of buffer)
*/
p++; /* eat (first) input slash */
DUK__ASSERT(q >= buf);
if (q == buf) {
goto resolve_error;
}
DUK__ASSERT(*(q - 1) == '/');
q--; /* Backtrack to last output slash (dups already eliminated). */
for (;;) {
/* Backtrack to previous slash or start of buffer. */
DUK__ASSERT(q >= buf);
if (q == buf) {
break;
}
if (*(q - 1) == '/') {
break;
}
q--;
}
goto eat_dup_slashes;
}
goto resolve_error;
} else if (c == '/') {
/* e.g. require('/foo'), empty terms not allowed */
goto resolve_error;
} else {
for (;;) {
/* Copy term name until end or '/'. */
*q++ = c;
c = *p++;
if (c == 0) {
/* This was the last term, and q_last was
* updated to match this term at loop top.
*/
goto loop_done;
} else if (c == '/') {
*q++ = '/';
break;
} else {
/* write on next loop */
}
}
}
eat_dup_slashes:
for (;;) {
/* eat dup slashes */
c = *p;
if (c != '/') {
break;
}
p++;
}
}
loop_done:
/* Output #1: resolved absolute name. */
DUK__ASSERT(q >= buf);
duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf));
/* Output #2: last component name. */
DUK__ASSERT(q >= q_last);
DUK__ASSERT(q_last >= buf);
duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last));
return;
resolve_error:
(void) duk_type_error(ctx, "cannot resolve module id: %s", (const char *) req_id);
}
/* Stack indices for better readability. */
#define DUK__IDX_REQUESTED_ID 0 /* module id requested */
#define DUK__IDX_REQUIRE 1 /* current require() function */
#define DUK__IDX_REQUIRE_ID 2 /* the base ID of the current require() function, resolution base */
#define DUK__IDX_RESOLVED_ID 3 /* resolved, normalized absolute module ID */
#define DUK__IDX_LASTCOMP 4 /* last component name in resolved path */
#define DUK__IDX_DUKTAPE 5 /* Duktape object */
#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */
#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */
#define DUK__IDX_FRESH_REQUIRE 8 /* new require() function for module, updated resolution base */
#define DUK__IDX_EXPORTS 9 /* default exports table */
#define DUK__IDX_MODULE 10 /* module object containing module.exports, etc */
static duk_ret_t duk__require(duk_context *ctx) {
const char *str_req_id; /* requested identifier */
const char *str_mod_id; /* require.id of current module */
duk_int_t pcall_rc;
/* NOTE: we try to minimize code size by avoiding unnecessary pops,
* so the stack looks a bit cluttered in this function. DUK__ASSERT_TOP()
* assertions are used to ensure stack configuration is correct at each
* step.
*/
/*
* Resolve module identifier into canonical absolute form.
*/
str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID);
duk_push_current_function(ctx);
duk_get_prop_string(ctx, -1, "id");
str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */
duk__resolve_module_id(ctx, str_req_id, str_mod_id);
str_req_id = NULL;
str_mod_id = NULL;
/* [ requested_id require require.id resolved_id last_comp ] */
DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1);
/*
* Cached module check.
*
* If module has been loaded or its loading has already begun without
* finishing, return the same cached value (module.exports). The
* value is registered when module load starts so that circular
* references can be supported to some extent.
*/
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, -1, "\xff" "module:Duktape");
duk_remove(ctx, -2); /* Lookup stashed, original 'Duktape' object. */
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded"); /* Duktape.modLoaded */
duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT);
DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1);
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) {
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
duk_get_prop_string(ctx, -1, "exports"); /* return module.exports */
return 1;
}
DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */
/*
* Module not loaded (and loading not started previously).
*
* Create a new require() function with 'id' set to resolved ID
* of module being loaded. Also create 'exports' and 'module'
* tables but don't register exports to the loaded table yet.
* We don't want to do that unless the user module search callbacks
* succeeds in finding the module.
*/
/* Fresh require: require.id is left configurable (but not writable)
* so that is not easy to accidentally tweak it, but it can still be
* done with Object.defineProperty().
*
* XXX: require.id could also be just made non-configurable, as there
* is no practical reason to touch it (at least from Ecmascript code).
*/
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
duk_push_string(ctx, "name");
duk_push_string(ctx, "require");
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE); /* not writable, not enumerable, not configurable */
duk_push_string(ctx, "id");
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE); /* a fresh require() with require.id = resolved target module id */
/* Module table:
* - module.exports: initial exports table (may be replaced by user)
* - module.id is non-writable and non-configurable, as the CommonJS
* spec suggests this if possible
* - module.filename: not set, defaults to resolved ID if not explicitly
* set by modSearch() (note capitalization, not .fileName, matches Node.js)
* - module.name: not set, defaults to last component of resolved ID if
* not explicitly set by modSearch()
*/
duk_push_object(ctx); /* exports */
duk_push_object(ctx); /* module */
duk_push_string(ctx, "exports");
duk_dup(ctx, DUK__IDX_EXPORTS);
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); /* module.exports = exports */
duk_push_string(ctx, "id");
duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE); /* module.id = resolved_id; not writable, not enumerable, not configurable */
duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 1);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */
/* Register the module table early to modLoaded[] so that we can
* support circular references even in modSearch(). If an error
* is thrown, we'll delete the reference.
*/
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_dup(ctx, DUK__IDX_MODULE);
duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */
/*
* Call user provided module search function and build the wrapped
* module source code (if necessary). The module search function
* can be used to implement pure Ecmacsript, pure C, and mixed
* Ecmascript/C modules.
*
* The module search function can operate on the exports table directly
* (e.g. DLL code can register values to it). It can also return a
* string which is interpreted as module source code (if a non-string
* is returned the module is assumed to be a pure C one). If a module
* cannot be found, an error must be thrown by the user callback.
*
* Because Duktape.modLoaded[] already contains the module being
* loaded, circular references for C modules should also work
* (although expected to be quite rare).
*/
duk_push_string(ctx, "(function(require,exports,module){");
/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch"); /* Duktape.modSearch */
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);
duk_dup(ctx, DUK__IDX_EXPORTS);
duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */
pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 3);
if (pcall_rc != DUK_EXEC_SUCCESS) {
/* Delete entry in Duktape.modLoaded[] and rethrow. */
goto delete_rethrow;
}
/* If user callback did not return source code, module loading
* is finished (user callback initialized exports table directly).
*/
if (!duk_is_string(ctx, -1)) {
/* User callback did not return source code, so module loading
* is finished: just update modLoaded with final module.exports
* and we're done.
*/
goto return_exports;
}
/* Finish the wrapped module source. Force module.filename as the
* function .fileName so it gets set for functions defined within a
* module. This also ensures loggers created within the module get
* the module ID (or overridden filename) as their default logger name.
* (Note capitalization: .filename matches Node.js while .fileName is
* used elsewhere in Duktape.)
*/
duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
duk_concat(ctx, 3);
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) {
/* module.filename for .fileName, default to resolved ID if
* not present.
*/
duk_pop(ctx);
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
}
pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
if (pcall_rc != DUK_EXEC_SUCCESS) {
goto delete_rethrow;
}
pcall_rc = duk_pcall(ctx, 0); /* -> eval'd function wrapper (not called yet) */
if (pcall_rc != DUK_EXEC_SUCCESS) {
goto delete_rethrow;
}
/* Module has now evaluated to a wrapped module function. Force its
* .name to match module.name (defaults to last component of resolved
* ID) so that it is shown in stack traces too. Note that we must not
* introduce an actual name binding into the function scope (which is
* usually the case with a named function) because it would affect the
* scope seen by the module and shadow accesses to globals of the same name.
* This is now done by compiling the function as anonymous and then forcing
* its .name without setting a "has name binding" flag.
*/
duk_push_string(ctx, "name");
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) {
/* module.name for .name, default to last component if
* not present.
*/
duk_pop(ctx);
duk_dup(ctx, DUK__IDX_LASTCOMP);
}
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
/*
* Call the wrapped module function.
*
* Use a protected call so that we can update Duktape.modLoaded[resolved_id]
* even if the module throws an error.
*/
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports"); /* relookup exports from module.exports in case it was changed by modSearch */
duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 6);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
if (pcall_rc != DUK_EXEC_SUCCESS) {
/* Module loading failed. Node.js will forget the module
* registration so that another require() will try to load
* the module again. Mimic that behavior.
*/
goto delete_rethrow;
}
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
/* fall through */
return_exports:
duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports");
duk_compact(ctx, -1); /* compact the exports table */
return 1; /* return module.exports */
delete_rethrow:
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */
(void) duk_throw(ctx); /* rethrow original error */
return 0; /* not reachable */
}
void duk_module_duktape_init(duk_context *ctx) {
/* Stash 'Duktape' in case it's modified. */
duk_push_global_stash(ctx);
duk_get_global_string(ctx, "Duktape");
duk_put_prop_string(ctx, -2, "\xff" "module:Duktape");
duk_pop(ctx);
/* Register `require` as a global function. */
duk_eval_string(ctx,
"(function(req){"
"var D=Object.defineProperty;"
"D(req,'name',{value:'require'});"
"D(this,'require',{value:req,writable:true,configurable:true});"
"D(Duktape,'modLoaded',{value:Object.create(null),writable:true,configurable:true});"
"})");
duk_push_c_function(ctx, duk__require, 1 /*nargs*/);
duk_call(ctx, 1);
duk_pop(ctx);
}
#undef DUK__ASSERT
#undef DUK__ASSERT_TOP
#undef DUK__IDX_REQUESTED_ID
#undef DUK__IDX_REQUIRE
#undef DUK__IDX_REQUIRE_ID
#undef DUK__IDX_RESOLVED_ID
#undef DUK__IDX_LASTCOMP
#undef DUK__IDX_DUKTAPE
#undef DUK__IDX_MODLOADED
#undef DUK__IDX_UNDEFINED
#undef DUK__IDX_FRESH_REQUIRE
#undef DUK__IDX_EXPORTS
#undef DUK__IDX_MODULE

14
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_duktape.h generated vendored Executable file
View File

@@ -0,0 +1,14 @@
#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED)
#define DUK_MODULE_DUKTAPE_H_INCLUDED
#include "duktape.h"
/* Maximum length of CommonJS module identifier to resolve. Length includes
* both current module ID, requested (possibly relative) module ID, and a
* slash in between.
*/
#define DUK_COMMONJS_MODULE_ID_LIMIT 256
extern void duk_module_duktape_init(duk_context *ctx);
#endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */

333
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_node.c generated vendored Executable file
View File

@@ -0,0 +1,333 @@
/*
* Node.js-like module loading framework for Duktape
*
* https://nodejs.org/api/modules.html
*/
#include "duktape.h"
#include "duk_module_node.h"
#if DUK_VERSION >= 19999
static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata);
#else
static duk_int_t duk__eval_module_source(duk_context *ctx);
#endif
static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main);
static duk_bool_t duk__get_cached_module(duk_context *ctx, const char *id) {
duk_push_global_stash(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
if (duk_get_prop_string(ctx, -1, id)) {
duk_remove(ctx, -2);
duk_remove(ctx, -2);
return 1;
} else {
duk_pop_3(ctx);
return 0;
}
}
/* Place a `module` object on the top of the value stack into the require cache
* based on its `.id` property. As a convenience to the caller, leave the
* object on top of the value stack afterwards.
*/
static void duk__put_cached_module(duk_context *ctx) {
/* [ ... module ] */
duk_push_global_stash(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
duk_dup(ctx, -3);
/* [ ... module stash req_cache module ] */
(void) duk_get_prop_string(ctx, -1, "id");
duk_dup(ctx, -2);
duk_put_prop(ctx, -4);
duk_pop_3(ctx); /* [ ... module ] */
}
static void duk__del_cached_module(duk_context *ctx, const char *id) {
duk_push_global_stash(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
duk_del_prop_string(ctx, -1, id);
duk_pop_2(ctx);
}
static duk_ret_t duk__handle_require(duk_context *ctx) {
/*
* Value stack handling here is a bit sloppy but should be correct.
* Call handling will clean up any extra garbage for us.
*/
const char *id;
const char *parent_id;
duk_idx_t module_idx;
duk_idx_t stash_idx;
duk_int_t ret;
duk_push_global_stash(ctx);
stash_idx = duk_normalize_index(ctx, -1);
duk_push_current_function(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "moduleId");
parent_id = duk_require_string(ctx, -1);
(void) parent_id; /* not used directly; suppress warning */
/* [ id stash require parent_id ] */
id = duk_require_string(ctx, 0);
(void) duk_get_prop_string(ctx, stash_idx, "\xff" "modResolve");
duk_dup(ctx, 0); /* module ID */
duk_dup(ctx, -3); /* parent ID */
duk_call(ctx, 2);
/* [ ... stash ... resolved_id ] */
id = duk_require_string(ctx, -1);
if (duk__get_cached_module(ctx, id)) {
goto have_module; /* use the cached module */
}
duk__push_module_object(ctx, id, 0 /*main*/);
duk__put_cached_module(ctx); /* module remains on stack */
/*
* From here on out, we have to be careful not to throw. If it can't be
* avoided, the error must be caught and the module removed from the
* require cache before rethrowing. This allows the application to
* reattempt loading the module.
*/
module_idx = duk_normalize_index(ctx, -1);
/* [ ... stash ... resolved_id module ] */
(void) duk_get_prop_string(ctx, stash_idx, "\xff" "modLoad");
duk_dup(ctx, -3); /* resolved ID */
(void) duk_get_prop_string(ctx, module_idx, "exports");
duk_dup(ctx, module_idx);
ret = duk_pcall(ctx, 3);
if (ret != DUK_EXEC_SUCCESS) {
duk__del_cached_module(ctx, id);
(void) duk_throw(ctx); /* rethrow */
}
if (duk_is_string(ctx, -1)) {
duk_int_t ret;
/* [ ... module source ] */
#if DUK_VERSION >= 19999
ret = duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
#else
ret = duk_safe_call(ctx, duk__eval_module_source, 2, 1);
#endif
if (ret != DUK_EXEC_SUCCESS) {
duk__del_cached_module(ctx, id);
(void) duk_throw(ctx); /* rethrow */
}
} else if (duk_is_undefined(ctx, -1)) {
duk_pop(ctx);
} else {
duk__del_cached_module(ctx, id);
(void) duk_type_error(ctx, "invalid module load callback return value");
}
/* fall through */
have_module:
/* [ ... module ] */
(void) duk_get_prop_string(ctx, -1, "exports");
return 1;
}
static void duk__push_require_function(duk_context *ctx, const char *id) {
duk_push_c_function(ctx, duk__handle_require, 1);
duk_push_string(ctx, "name");
duk_push_string(ctx, "require");
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, id);
duk_put_prop_string(ctx, -2, "\xff" "moduleId");
/* require.cache */
duk_push_global_stash(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "requireCache");
duk_put_prop_string(ctx, -3, "cache");
duk_pop(ctx);
/* require.main */
duk_push_global_stash(ctx);
(void) duk_get_prop_string(ctx, -1, "\xff" "mainModule");
duk_put_prop_string(ctx, -3, "main");
duk_pop(ctx);
}
static void duk__push_module_object(duk_context *ctx, const char *id, duk_bool_t main) {
duk_push_object(ctx);
/* Set this as the main module, if requested */
if (main) {
duk_push_global_stash(ctx);
duk_dup(ctx, -2);
duk_put_prop_string(ctx, -2, "\xff" "mainModule");
duk_pop(ctx);
}
/* Node.js uses the canonicalized filename of a module for both module.id
* and module.filename. We have no concept of a file system here, so just
* use the module ID for both values.
*/
duk_push_string(ctx, id);
duk_dup(ctx, -1);
duk_put_prop_string(ctx, -3, "filename");
duk_put_prop_string(ctx, -2, "id");
/* module.exports = {} */
duk_push_object(ctx);
duk_put_prop_string(ctx, -2, "exports");
/* module.loaded = false */
duk_push_false(ctx);
duk_put_prop_string(ctx, -2, "loaded");
/* module.require */
duk__push_require_function(ctx, id);
duk_put_prop_string(ctx, -2, "require");
}
#if DUK_VERSION >= 19999
static duk_int_t duk__eval_module_source(duk_context *ctx, void *udata) {
#else
static duk_int_t duk__eval_module_source(duk_context *ctx) {
#endif
const char *src;
/*
* Stack: [ ... module source ]
*/
#if DUK_VERSION >= 19999
(void) udata;
#endif
/* Wrap the module code in a function expression. This is the simplest
* way to implement CommonJS closure semantics and matches the behavior of
* e.g. Node.js.
*/
duk_push_string(ctx, "(function(exports,require,module,__filename,__dirname){");
src = duk_require_string(ctx, -2);
duk_push_string(ctx, (src[0] == '#' && src[1] == '!') ? "//" : ""); /* Shebang support. */
duk_dup(ctx, -3); /* source */
duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */
duk_concat(ctx, 4);
/* [ ... module source func_src ] */
(void) duk_get_prop_string(ctx, -3, "filename");
duk_compile(ctx, DUK_COMPILE_EVAL);
duk_call(ctx, 0);
/* [ ... module source func ] */
/* Set name for the wrapper function. */
duk_push_string(ctx, "name");
duk_push_string(ctx, "main");
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
/* call the function wrapper */
(void) duk_get_prop_string(ctx, -3, "exports"); /* exports */
(void) duk_get_prop_string(ctx, -4, "require"); /* require */
duk_dup(ctx, -5); /* module */
(void) duk_get_prop_string(ctx, -6, "filename"); /* __filename */
duk_push_undefined(ctx); /* __dirname */
duk_call(ctx, 5);
/* [ ... module source result(ignore) ] */
/* module.loaded = true */
duk_push_true(ctx);
duk_put_prop_string(ctx, -4, "loaded");
/* [ ... module source retval ] */
duk_pop_2(ctx);
/* [ ... module ] */
return 1;
}
/* Load a module as the 'main' module. */
duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path) {
/*
* Stack: [ ... source ]
*/
duk__push_module_object(ctx, path, 1 /*main*/);
/* [ ... source module ] */
duk_dup(ctx, 0);
/* [ ... source module source ] */
#if DUK_VERSION >= 19999
return duk_safe_call(ctx, duk__eval_module_source, NULL, 2, 1);
#else
return duk_safe_call(ctx, duk__eval_module_source, 2, 1);
#endif
}
void duk_module_node_init(duk_context *ctx) {
/*
* Stack: [ ... options ] => [ ... ]
*/
duk_idx_t options_idx;
duk_require_object_coercible(ctx, -1); /* error before setting up requireCache */
options_idx = duk_require_normalize_index(ctx, -1);
/* Initialize the require cache to a fresh object. */
duk_push_global_stash(ctx);
#if DUK_VERSION >= 19999
duk_push_bare_object(ctx);
#else
duk_push_object(ctx);
duk_push_undefined(ctx);
duk_set_prototype(ctx, -2);
#endif
duk_put_prop_string(ctx, -2, "\xff" "requireCache");
duk_pop(ctx);
/* Stash callbacks for later use. User code can overwrite them later
* on directly by accessing the global stash.
*/
duk_push_global_stash(ctx);
duk_get_prop_string(ctx, options_idx, "resolve");
duk_require_function(ctx, -1);
duk_put_prop_string(ctx, -2, "\xff" "modResolve");
duk_get_prop_string(ctx, options_idx, "load");
duk_require_function(ctx, -1);
duk_put_prop_string(ctx, -2, "\xff" "modLoad");
duk_pop(ctx);
/* Stash main module. */
duk_push_global_stash(ctx);
duk_push_undefined(ctx);
duk_put_prop_string(ctx, -2, "\xff" "mainModule");
duk_pop(ctx);
/* register `require` as a global function. */
duk_push_global_object(ctx);
duk_push_string(ctx, "require");
duk__push_require_function(ctx, "");
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |
DUK_DEFPROP_SET_WRITABLE |
DUK_DEFPROP_SET_CONFIGURABLE);
duk_pop(ctx);
duk_pop(ctx); /* pop argument */
}

9
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_node.h generated vendored Executable file
View File

@@ -0,0 +1,9 @@
#if !defined(DUK_MODULE_NODE_H_INCLUDED)
#define DUK_MODULE_NODE_H_INCLUDED
#include "duktape.h"
extern duk_ret_t duk_module_node_peval_main(duk_context *ctx, const char *path);
extern void duk_module_node_init(duk_context *ctx);
#endif /* DUK_MODULE_NODE_H_INCLUDED */

127
vendor/gopkg.in/olebedev/go-duktape.v3/duk_print_alert.c generated vendored Executable file
View File

@@ -0,0 +1,127 @@
/*
* Duktape 1.x compatible print() and alert() bindings.
*/
#include <stdio.h>
#include <string.h>
#include "duktape.h"
#include "duk_print_alert.h"
#define DUK_PRINT_ALERT_FLUSH /* Flush after stdout/stderr write (Duktape 1.x: yes) */
#undef DUK_PRINT_ALERT_SMALL /* Prefer smaller footprint (but slower and more memory churn) */
#if defined(DUK_PRINT_ALERT_SMALL)
static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) {
duk_idx_t nargs;
nargs = duk_get_top(ctx);
/* If argument count is 1 and first argument is a buffer, write the buffer
* as raw data into the file without a newline; this allows exact control
* over stdout/stderr without an additional entrypoint (useful for now).
* Otherwise current print/alert semantics are to ToString() coerce
* arguments, join them with a single space, and append a newline.
*/
if (nargs == 1 && duk_is_buffer(ctx, 0)) {
buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf);
fwrite((const void *) buf, 1, (size_t) sz_buf, fh);
} else {
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_concat(ctx, nargs);
fprintf(fh, "%s\n", duk_require_string(ctx, -1));
}
#if defined(DUK_PRINT_ALERT_FLUSH)
fflush(fh);
#endif
return 0;
}
#else
/* Faster, less churn, higher footprint option. */
static duk_ret_t duk__print_alert_helper(duk_context *ctx, FILE *fh) {
duk_idx_t nargs;
const duk_uint8_t *buf;
duk_size_t sz_buf;
const char nl = (const char) '\n';
duk_uint8_t buf_stack[256];
nargs = duk_get_top(ctx);
/* If argument count is 1 and first argument is a buffer, write the buffer
* as raw data into the file without a newline; this allows exact control
* over stdout/stderr without an additional entrypoint (useful for now).
* Otherwise current print/alert semantics are to ToString() coerce
* arguments, join them with a single space, and append a newline.
*/
if (nargs == 1 && duk_is_buffer(ctx, 0)) {
buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf);
} else if (nargs > 0) {
duk_idx_t i;
duk_size_t sz_str;
const duk_uint8_t *p_str;
duk_uint8_t *p;
sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */
for (i = 0; i < nargs; i++) {
(void) duk_to_lstring(ctx, i, &sz_str);
sz_buf += sz_str;
}
if (sz_buf <= sizeof(buf_stack)) {
p = (duk_uint8_t *) buf_stack;
} else {
p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf);
}
buf = (const duk_uint8_t *) p;
for (i = 0; i < nargs; i++) {
p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str);
memcpy((void *) p, (const void *) p_str, sz_str);
p += sz_str;
*p++ = (duk_uint8_t) (i == nargs - 1 ? '\n' : ' ');
}
} else {
buf = (const duk_uint8_t *) &nl;
sz_buf = 1;
}
/* 'buf' contains the string to write, 'sz_buf' contains the length
* (which may be zero).
*/
if (sz_buf > 0) {
fwrite((const void *) buf, 1, (size_t) sz_buf, fh);
#if defined(DUK_PRINT_ALERT_FLUSH)
fflush(fh);
#endif
}
return 0;
}
#endif
static duk_ret_t duk__print(duk_context *ctx) {
return duk__print_alert_helper(ctx, stdout);
}
static duk_ret_t duk__alert(duk_context *ctx) {
return duk__print_alert_helper(ctx, stderr);
}
void duk_print_alert_init(duk_context *ctx, duk_uint_t flags) {
(void) flags; /* unused at the moment */
/* XXX: use duk_def_prop_list(). */
duk_push_global_object(ctx);
duk_push_string(ctx, "print");
duk_push_c_function(ctx, duk__print, DUK_VARARGS);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
duk_push_string(ctx, "alert");
duk_push_c_function(ctx, duk__alert, DUK_VARARGS);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE);
duk_pop(ctx);
}

10
vendor/gopkg.in/olebedev/go-duktape.v3/duk_print_alert.h generated vendored Executable file
View File

@@ -0,0 +1,10 @@
#if !defined(DUK_PRINT_ALERT_H_INCLUDED)
#define DUK_PRINT_ALERT_H_INCLUDED
#include "duktape.h"
/* No flags at the moment. */
extern void duk_print_alert_init(duk_context *ctx, duk_uint_t flags);
#endif /* DUK_PRINT_ALERT_H_INCLUDED */

131
vendor/gopkg.in/olebedev/go-duktape.v3/duk_v1_compat.c generated vendored Executable file
View File

@@ -0,0 +1,131 @@
#include <stdio.h>
#include "duktape.h"
#include "duk_v1_compat.h"
/*
* duk_dump_context_{stdout,stderr}()
*/
void duk_dump_context_stdout(duk_context *ctx) {
duk_push_context_dump(ctx);
fprintf(stdout, "%s\n", duk_safe_to_string(ctx, -1));
duk_pop(ctx);
}
void duk_dump_context_stderr(duk_context *ctx) {
duk_push_context_dump(ctx);
fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1));
duk_pop(ctx);
}
/*
* duk_push_string_file() and duk_push_string_file_raw()
*/
const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
FILE *f = NULL;
char *buf;
long sz; /* ANSI C typing */
if (!path) {
goto fail;
}
f = fopen(path, "rb");
if (!f) {
goto fail;
}
if (fseek(f, 0, SEEK_END) < 0) {
goto fail;
}
sz = ftell(f);
if (sz < 0) {
goto fail;
}
if (fseek(f, 0, SEEK_SET) < 0) {
goto fail;
}
buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz);
if ((size_t) fread(buf, 1, (size_t) sz, f) != (size_t) sz) {
duk_pop(ctx);
goto fail;
}
(void) fclose(f); /* ignore fclose() error */
return duk_buffer_to_string(ctx, -1);
fail:
if (f) {
(void) fclose(f); /* ignore fclose() error */
}
if (flags & DUK_STRING_PUSH_SAFE) {
duk_push_undefined(ctx);
} else {
(void) duk_type_error(ctx, "read file error");
}
return NULL;
}
/*
* duk_eval_file(), duk_compile_file(), and their variants
*/
void duk_eval_file(duk_context *ctx, const char *path) {
duk_push_string_file_raw(ctx, path, 0);
duk_push_string(ctx, path);
duk_compile(ctx, DUK_COMPILE_EVAL);
duk_push_global_object(ctx); /* 'this' binding */
duk_call_method(ctx, 0);
}
void duk_eval_file_noresult(duk_context *ctx, const char *path) {
duk_eval_file(ctx, path);
duk_pop(ctx);
}
duk_int_t duk_peval_file(duk_context *ctx, const char *path) {
duk_int_t rc;
duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
duk_push_string(ctx, path);
rc = duk_pcompile(ctx, DUK_COMPILE_EVAL);
if (rc != 0) {
return rc;
}
duk_push_global_object(ctx); /* 'this' binding */
rc = duk_pcall_method(ctx, 0);
return rc;
}
duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path) {
duk_int_t rc;
rc = duk_peval_file(ctx, path);
duk_pop(ctx);
return rc;
}
void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
duk_push_string_file_raw(ctx, path, 0);
duk_push_string(ctx, path);
duk_compile(ctx, flags);
}
duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path) {
duk_int_t rc;
duk_push_string_file_raw(ctx, path, DUK_STRING_PUSH_SAFE);
duk_push_string(ctx, path);
rc = duk_pcompile(ctx, flags);
return rc;
}
/*
* duk_to_defaultvalue()
*/
void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint) {
duk_require_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT |
DUK_TYPE_MASK_BUFFER |
DUK_TYPE_MASK_LIGHTFUNC);
duk_to_primitive(ctx, idx, hint);
}

28
vendor/gopkg.in/olebedev/go-duktape.v3/duk_v1_compat.h generated vendored Executable file
View File

@@ -0,0 +1,28 @@
#if !defined(DUK_V1_COMPAT_INCLUDED)
#define DUK_V1_COMPAT_INCLUDED
#include "duktape.h"
/* Straight flag rename */
#if !defined(DUK_ENUM_INCLUDE_INTERNAL)
#define DUK_ENUM_INCLUDE_INTERNAL DUK_ENUM_INCLUDE_HIDDEN
#endif
/* Flags for duk_push_string_file_raw() */
#define DUK_STRING_PUSH_SAFE (1 << 0) /* no error if file does not exist */
extern void duk_dump_context_stdout(duk_context *ctx);
extern void duk_dump_context_stderr(duk_context *ctx);
extern const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags);
extern void duk_eval_file(duk_context *ctx, const char *path);
extern void duk_eval_file_noresult(duk_context *ctx, const char *path);
extern duk_int_t duk_peval_file(duk_context *ctx, const char *path);
extern duk_int_t duk_peval_file_noresult(duk_context *ctx, const char *path);
extern void duk_compile_file(duk_context *ctx, duk_uint_t flags, const char *path);
extern duk_int_t duk_pcompile_file(duk_context *ctx, duk_uint_t flags, const char *path);
extern void duk_to_defaultvalue(duk_context *ctx, duk_idx_t idx, duk_int_t hint);
#define duk_push_string_file(ctx,path) \
duk_push_string_file_raw((ctx), (path), 0)
#endif /* DUK_V1_COMPAT_INCLUDED */

95118
vendor/gopkg.in/olebedev/go-duktape.v3/duktape.c generated vendored Executable file

File diff suppressed because it is too large Load Diff

356
vendor/gopkg.in/olebedev/go-duktape.v3/duktape.go generated vendored Normal file
View File

@@ -0,0 +1,356 @@
package duktape
/*
#cgo !windows CFLAGS: -std=c99 -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
#cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
#cgo linux LDFLAGS: -lm
#cgo freebsd LDFLAGS: -lm
#include "duktape.h"
#include "duk_logging.h"
#include "duk_print_alert.h"
#include "duk_module_duktape.h"
#include "duk_console.h"
extern duk_ret_t goFunctionCall(duk_context *ctx);
extern void goFinalizeCall(duk_context *ctx);
*/
import "C"
import (
"errors"
"fmt"
"regexp"
"sync"
"unsafe"
)
var reFuncName = regexp.MustCompile("^[a-z_][a-z0-9_]*([A-Z_][a-z0-9_]*)*$")
const (
goFunctionPtrProp = "\xff" + "goFunctionPtrProp"
goContextPtrProp = "\xff" + "goContextPtrProp"
)
type Context struct {
*context
}
// transmute replaces the value from Context with the value of pointer
func (c *Context) transmute(p unsafe.Pointer) {
*c = *(*Context)(p)
}
// this is a pojo containing only the values of the Context
type context struct {
sync.Mutex
duk_context *C.duk_context
fnIndex *functionIndex
timerIndex *timerIndex
}
// New returns plain initialized duktape context object
// See: http://duktape.org/api.html#duk_create_heap_default
func New() *Context {
d := &Context{
&context{
duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
fnIndex: newFunctionIndex(),
timerIndex: &timerIndex{},
},
}
ctx := d.duk_context
C.duk_logging_init(ctx, 0)
C.duk_print_alert_init(ctx, 0)
C.duk_module_duktape_init(ctx)
C.duk_console_init(ctx, 0)
return d
}
// Flags is a set of flags for controlling the behaviour of duktape.
type Flags struct {
Logging uint
PrintAlert uint
Console uint
}
// FlagConsoleProxyWrapper is a Console flag.
// Use a proxy wrapper to make undefined methods (console.foo()) no-ops.
const FlagConsoleProxyWrapper = 1 << 0
// FlagConsoleFlush is a Console flag.
// Flush output after every call.
const FlagConsoleFlush = 1 << 1
// NewWithFlags returns plain initialized duktape context object
// You can control the behaviour of duktape by setting flags.
// See: http://duktape.org/api.html#duk_create_heap_default
func NewWithFlags(flags *Flags) *Context {
d := &Context{
&context{
duk_context: C.duk_create_heap(nil, nil, nil, nil, nil),
fnIndex: newFunctionIndex(),
timerIndex: &timerIndex{},
},
}
ctx := d.duk_context
C.duk_logging_init(ctx, C.duk_uint_t(flags.Logging))
C.duk_print_alert_init(ctx, C.duk_uint_t(flags.PrintAlert))
C.duk_module_duktape_init(ctx)
C.duk_console_init(ctx, C.duk_uint_t(flags.Console))
return d
}
func contextFromPointer(ctx *C.duk_context) *Context {
return &Context{&context{duk_context: ctx}}
}
// PushGlobalGoFunction push the given function into duktape global object
// Returns non-negative index (relative to stack bottom) of the pushed function
// also returns error if the function name is invalid
func (d *Context) PushGlobalGoFunction(name string, fn func(*Context) int) (int, error) {
if !reFuncName.MatchString(name) {
return -1, errors.New("Malformed function name '" + name + "'")
}
d.PushGlobalObject()
idx := d.PushGoFunction(fn)
d.PutPropString(-2, name)
d.Pop()
return idx, nil
}
// PushGoFunction push the given function into duktape stack, returns non-negative
// index (relative to stack bottom) of the pushed function
func (d *Context) PushGoFunction(fn func(*Context) int) int {
funPtr := d.fnIndex.add(fn)
ctxPtr := contexts.add(d)
idx := d.PushCFunction((*[0]byte)(C.goFunctionCall), C.DUK_VARARGS)
d.PushCFunction((*[0]byte)(C.goFinalizeCall), 1)
d.PushPointer(funPtr)
d.PutPropString(-2, goFunctionPtrProp)
d.PushPointer(ctxPtr)
d.PutPropString(-2, goContextPtrProp)
d.SetFinalizer(-2)
d.PushPointer(funPtr)
d.PutPropString(-2, goFunctionPtrProp)
d.PushPointer(ctxPtr)
d.PutPropString(-2, goContextPtrProp)
return idx
}
//export goFunctionCall
func goFunctionCall(cCtx *C.duk_context) C.duk_ret_t {
d := contextFromPointer(cCtx)
funPtr, ctx := d.getFunctionPtrs()
d.transmute(unsafe.Pointer(ctx))
result := d.fnIndex.get(funPtr)(d)
return C.duk_ret_t(result)
}
//export goFinalizeCall
func goFinalizeCall(cCtx *C.duk_context) {
d := contextFromPointer(cCtx)
funPtr, ctx := d.getFunctionPtrs()
d.transmute(unsafe.Pointer(ctx))
d.fnIndex.delete(funPtr)
}
func (d *Context) getFunctionPtrs() (unsafe.Pointer, *Context) {
d.PushCurrentFunction()
d.GetPropString(-1, goFunctionPtrProp)
funPtr := d.GetPointer(-1)
d.Pop()
d.GetPropString(-1, goContextPtrProp)
ctx := contexts.get(d.GetPointer(-1))
d.Pop2()
return funPtr, ctx
}
// Destroy destroy all the references to the functions and freed the pointers
func (d *Context) Destroy() {
d.fnIndex.destroy()
contexts.delete(d)
}
type Error struct {
Type string
Message string
FileName string
LineNumber int
Stack string
}
func (e *Error) Error() string {
return fmt.Sprintf("%s: %s", e.Type, e.Message)
}
type Type int
func (t Type) IsNone() bool { return t == TypeNone }
func (t Type) IsUndefined() bool { return t == TypeUndefined }
func (t Type) IsNull() bool { return t == TypeNull }
func (t Type) IsBool() bool { return t == TypeBoolean }
func (t Type) IsNumber() bool { return t == TypeNumber }
func (t Type) IsString() bool { return t == TypeString }
func (t Type) IsObject() bool { return t == TypeObject }
func (t Type) IsBuffer() bool { return t == TypeBuffer }
func (t Type) IsPointer() bool { return t == TypePointer }
func (t Type) IsLightFunc() bool { return t == TypeLightFunc }
func (t Type) String() string {
switch t {
case TypeNone:
return "None"
case TypeUndefined:
return "Undefined"
case TypeNull:
return "Null"
case TypeBoolean:
return "Boolean"
case TypeNumber:
return "Number"
case TypeString:
return "String"
case TypeObject:
return "Object"
case TypeBuffer:
return "Buffer"
case TypePointer:
return "Pointer"
case TypeLightFunc:
return "LightFunc"
default:
return "Unknown"
}
}
type functionIndex struct {
functions map[unsafe.Pointer]func(*Context) int
sync.RWMutex
}
type timerIndex struct {
c float64
sync.Mutex
}
func (t *timerIndex) get() float64 {
t.Lock()
defer t.Unlock()
t.c++
return t.c
}
func newFunctionIndex() *functionIndex {
return &functionIndex{
functions: make(map[unsafe.Pointer]func(*Context) int, 0),
}
}
func (i *functionIndex) add(fn func(*Context) int) unsafe.Pointer {
ptr := C.malloc(1)
i.Lock()
i.functions[ptr] = fn
i.Unlock()
return ptr
}
func (i *functionIndex) get(ptr unsafe.Pointer) func(*Context) int {
i.RLock()
fn := i.functions[ptr]
i.RUnlock()
return fn
}
func (i *functionIndex) delete(ptr unsafe.Pointer) {
i.Lock()
delete(i.functions, ptr)
i.Unlock()
C.free(ptr)
}
func (i *functionIndex) destroy() {
i.Lock()
for ptr, _ := range i.functions {
delete(i.functions, ptr)
C.free(ptr)
}
i.Unlock()
}
type ctxIndex struct {
sync.RWMutex
ctxs map[unsafe.Pointer]*Context
}
func (ci *ctxIndex) add(ctx *Context) unsafe.Pointer {
ci.RLock()
for ptr, ctxPtr := range ci.ctxs {
if ctxPtr == ctx {
ci.RUnlock()
return ptr
}
}
ci.RUnlock()
ci.Lock()
for ptr, ctxPtr := range ci.ctxs {
if ctxPtr == ctx {
ci.Unlock()
return ptr
}
}
ptr := C.malloc(1)
ci.ctxs[ptr] = ctx
ci.Unlock()
return ptr
}
func (ci *ctxIndex) get(ptr unsafe.Pointer) *Context {
ci.RLock()
ctx := ci.ctxs[ptr]
ci.RUnlock()
return ctx
}
func (ci *ctxIndex) delete(ctx *Context) {
ci.Lock()
for ptr, ctxPtr := range ci.ctxs {
if ctxPtr == ctx {
delete(ci.ctxs, ptr)
C.free(ptr)
ci.Unlock()
return
}
}
panic(fmt.Sprintf("context (%p) doesn't exist", ctx))
}
var contexts *ctxIndex
func init() {
contexts = &ctxIndex{
ctxs: make(map[unsafe.Pointer]*Context),
}
}

1349
vendor/gopkg.in/olebedev/go-duktape.v3/duktape.h generated vendored Executable file

File diff suppressed because it is too large Load Diff

136
vendor/gopkg.in/olebedev/go-duktape.v3/timers.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
package duktape
import (
"errors"
"fmt"
"time"
)
// DefineTimers defines `setTimeout`, `clearTimeout`, `setInterval`,
// `clearInterval` into global context.
func (d *Context) PushTimers() error {
d.PushGlobalStash()
// check if timers already exists
if !d.HasPropString(-1, "timers") {
d.PushObject()
d.PutPropString(-2, "timers") // stash -> [ timers:{} ]
d.Pop()
d.PushGlobalGoFunction("setTimeout", setTimeout)
d.PushGlobalGoFunction("setInterval", setInterval)
d.PushGlobalGoFunction("clearTimeout", clearTimeout)
d.PushGlobalGoFunction("clearInterval", clearTimeout)
return nil
} else {
d.Pop()
return errors.New("Timers are already defined")
}
}
func (d *Context) FlushTimers() {
d.PushGlobalStash()
d.PushObject()
d.PutPropString(-2, "timers") // stash -> [ timers:{} ]
d.Pop()
}
func setTimeout(c *Context) int {
id := c.pushTimer(0)
timeout := c.ToNumber(1)
if timeout < 1 {
timeout = 1
}
go func(id float64) {
<-time.After(time.Duration(timeout) * time.Millisecond)
c.Lock()
defer c.Unlock()
if c.duk_context == nil {
fmt.Println("[duktape] Warning!\nsetTimeout invokes callback after the context was destroyed.")
return
}
// check if timer still exists
c.putTimer(id)
if c.GetType(-1).IsObject() {
c.Pcall(0 /* nargs */)
}
c.dropTimer(id)
}(id)
c.PushNumber(id)
return 1
}
func clearTimeout(c *Context) int {
if c.GetType(0).IsNumber() {
c.dropTimer(c.GetNumber(0))
c.Pop()
}
return 0
}
func setInterval(c *Context) int {
id := c.pushTimer(0)
timeout := c.ToNumber(1)
if timeout < 1 {
timeout = 1
}
go func(id float64) {
ticker := time.NewTicker(time.Duration(timeout) * time.Millisecond)
for _ = range ticker.C {
c.Lock()
// check if duktape context exists
if c.duk_context == nil {
c.dropTimer(id)
c.Pop()
ticker.Stop()
fmt.Println("[duktape] Warning!\nsetInterval invokes callback after the context was destroyed.")
c.Unlock()
continue
}
// check if timer still exists
c.putTimer(id)
if c.GetType(-1).IsObject() {
c.Pcall(0 /* nargs */)
c.Pop()
} else {
c.dropTimer(id)
c.Pop()
ticker.Stop()
}
c.Unlock()
}
}(id)
c.PushNumber(id)
return 1
}
func (d *Context) pushTimer(index int) float64 {
id := d.timerIndex.get()
d.PushGlobalStash()
d.GetPropString(-1, "timers")
d.PushNumber(id)
d.Dup(index)
d.PutProp(-3)
d.Pop2()
return id
}
func (d *Context) dropTimer(id float64) {
d.PushGlobalStash()
d.GetPropString(-1, "timers")
d.PushNumber(id)
d.DelProp(-2)
d.Pop2()
}
func (d *Context) putTimer(id float64) {
d.PushGlobalStash() // stash -> [ ..., timers: { <id>: { func: true } } ]
d.GetPropString(-1, "timers") // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true } ]
d.PushNumber(id)
d.GetProp(-2) // stash -> [ ..., timers: { <id>: { func: true } } }, { <id>: { func: true }, { func: true } ]
d.Replace(-3)
d.Pop()
}

10
vendor/gopkg.in/olebedev/go-duktape.v3/utils.go generated vendored Normal file
View File

@@ -0,0 +1,10 @@
package duktape
// Must returns existing *Context or throw panic.
// It is highly recommended to use Must all the time.
func (d *Context) Must() *Context {
if d.duk_context == nil {
panic("[duktape] Context does not exists!\nYou cannot call any contexts methods after `DestroyHeap()` was called.")
}
return d
}

14
vendor/gopkg.in/olebedev/go-duktape.v3/wercker.yml generated vendored Normal file
View File

@@ -0,0 +1,14 @@
box: golang
build:
steps:
- setup-go-workspace
- script:
name: go get
code: |
cd $WERCKER_SOURCE_DIR
go version
go get gopkg.in/check.v1
- script:
name: go test
code: |
go test . -v

6
vendor/vendor.json vendored
View File

@@ -693,6 +693,12 @@
"revision": "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6",
"revisionTime": "2016-06-21T03:49:01Z"
},
{
"checksumSHA1": "YvuEbc0a032zr+BTp/YbBQojiuY=",
"path": "gopkg.in/olebedev/go-duktape.v3",
"revision": "9af39127cb024b355a6a0221769f6ddfe3f542e7",
"revisionTime": "2017-12-20T12:19:14Z"
},
{
"checksumSHA1": "4BwmmgQUhWtizsR2soXND0nqZ1I=",
"path": "gopkg.in/sourcemap.v1",