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:
21
vendor/gopkg.in/olebedev/go-duktape.v3/Gopkg.lock
generated
vendored
Normal file
21
vendor/gopkg.in/olebedev/go-duktape.v3/Gopkg.lock
generated
vendored
Normal 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
3
vendor/gopkg.in/olebedev/go-duktape.v3/Gopkg.toml
generated
vendored
Normal 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
21
vendor/gopkg.in/olebedev/go-duktape.v3/LICENSE.md
generated
vendored
Normal 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
124
vendor/gopkg.in/olebedev/go-duktape.v3/README.md
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
# Duktape bindings for Go(Golang)
|
||||
|
||||
[](https://app.wercker.com/project/bykey/3a5bb2e639a4b4efaf4c8bf7cab7442d)
|
||||
[](https://travis-ci.org/olebedev/go-duktape)
|
||||
[](https://ci.appveyor.com/project/olebedev/go-duktape/branch/v3)
|
||||
[](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
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
34
vendor/gopkg.in/olebedev/go-duktape.v3/appveyor.yml
generated
vendored
Normal 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
121
vendor/gopkg.in/olebedev/go-duktape.v3/conts.go
generated
vendored
Normal 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
612
vendor/gopkg.in/olebedev/go-duktape.v3/duk_alloc_pool.c
generated
vendored
Executable 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
223
vendor/gopkg.in/olebedev/go-duktape.v3/duk_alloc_pool.h
generated
vendored
Executable 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
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
163
vendor/gopkg.in/olebedev/go-duktape.v3/duk_console.c
generated
vendored
Executable 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
14
vendor/gopkg.in/olebedev/go-duktape.v3/duk_console.h
generated
vendored
Executable 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
380
vendor/gopkg.in/olebedev/go-duktape.v3/duk_logging.c
generated
vendored
Executable 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
20
vendor/gopkg.in/olebedev/go-duktape.v3/duk_logging.h
generated
vendored
Executable 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
312
vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.c
generated
vendored
Executable 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
12
vendor/gopkg.in/olebedev/go-duktape.v3/duk_minimal_printf.h
generated
vendored
Executable 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
471
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_duktape.c
generated
vendored
Executable 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
14
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_duktape.h
generated
vendored
Executable 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
333
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_node.c
generated
vendored
Executable 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
9
vendor/gopkg.in/olebedev/go-duktape.v3/duk_module_node.h
generated
vendored
Executable 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
127
vendor/gopkg.in/olebedev/go-duktape.v3/duk_print_alert.c
generated
vendored
Executable 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
10
vendor/gopkg.in/olebedev/go-duktape.v3/duk_print_alert.h
generated
vendored
Executable 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
131
vendor/gopkg.in/olebedev/go-duktape.v3/duk_v1_compat.c
generated
vendored
Executable 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
28
vendor/gopkg.in/olebedev/go-duktape.v3/duk_v1_compat.h
generated
vendored
Executable 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
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
356
vendor/gopkg.in/olebedev/go-duktape.v3/duktape.go
generated
vendored
Normal 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
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
136
vendor/gopkg.in/olebedev/go-duktape.v3/timers.go
generated
vendored
Normal 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
10
vendor/gopkg.in/olebedev/go-duktape.v3/utils.go
generated
vendored
Normal 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
14
vendor/gopkg.in/olebedev/go-duktape.v3/wercker.yml
generated
vendored
Normal 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
6
vendor/vendor.json
vendored
@@ -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",
|
||||
|
Reference in New Issue
Block a user