1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-26 00:59:24 -05:00

Delete go implementation (#276)

The go prototype will remain at https://github.com/ry/deno/tree/golang
This commit is contained in:
Ryan Dahl 2018-06-22 14:23:42 +02:00 committed by GitHub
parent ef9dc2464e
commit 86354a29a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 114 additions and 7988 deletions

8
.gitignore vendored
View file

@ -1,8 +0,0 @@
node_modules/
.cache/
dist/
deno
assets.go
msg.pb.go
/msg.pb.js
/msg.pb.d.ts

View file

@ -44,7 +44,8 @@ install:
- gn args $BUILD_PATH --list
- ccache -s
# Travis hangs without -j2 argument to ninja.
- ninja -j2 -C $BUILD_PATH mock_runtime_test deno deno_rs
- ninja -j2 -C $BUILD_PATH mock_runtime_test deno
- ninja -j2 -C $BUILD_PATH deno_rs
script:
- $BUILD_PATH/mock_runtime_test
- $BUILD_PATH/deno foo bar

View file

@ -1,83 +0,0 @@
TS_FILES = \
console.ts \
deno.d.ts \
deno.ts \
dispatch.ts \
fetch.ts \
globals.ts \
main.ts \
msg.pb.d.ts \
msg.pb.js \
os.ts \
runtime.ts \
text-encoding.d.ts \
timers.ts \
tsconfig.json \
types.ts \
url.js \
util.ts \
v8_source_maps.ts \
v8worker2.d.ts
GO_FILES = \
cmd/main.go \
assets.go \
deno_dir.go \
deno_dir_test.go \
dispatch.go \
echo.go \
fetch.go \
main.go \
msg.pb.go \
os.go \
os_test.go \
timers.go \
util.go \
util_test.go \
integration_test.go
deno: msg.pb.go $(GO_FILES)
go build -o deno ./cmd
assets.go: dist/main.js
cp node_modules/typescript/lib/*d.ts dist/
cp deno.d.ts dist/
go-bindata -pkg deno -o assets.go dist/
msg.pb.go: msg.proto
protoc --go_out=. msg.proto
msg.pb.js: msg.proto node_modules
./node_modules/.bin/pbjs -t static-module -w commonjs -o msg.pb.js msg.proto
msg.pb.d.ts: msg.pb.js node_modules
./node_modules/.bin/pbts -o msg.pb.d.ts msg.pb.js
dist/main.js: $(TS_FILES) node_modules
./node_modules/.bin/tsc --noEmit # Only for type checking.
./node_modules/.bin/parcel build --out-dir=dist/ --log-level=1 --no-minify main.ts
node_modules:
yarn
clean:
-rm -f deno assets.go msg.pb.go msg.pb.js msg.pb.d.ts
-rm -rf dist/
distclean: clean
-rm -rf node_modules/
lint: node_modules
yarn lint
go vet
fmt: node_modules
yarn fmt
go fmt
clang-format msg.proto -i
test: deno
go test -v
.PHONY: test lint clean distclean

View file

@ -72,79 +72,20 @@ includes submitting trivial PRs (like improving README build instructions).
## Compile instructions
I will release binaries at some point, but for now you have to build it
yourself.
First install the javascript deps.
You will need [Go](https://golang.org) with `$GOPATH` defined and
`$GOPATH/bin` in your `$PATH`.
cd deno2
You will also need [yarn](https://yarnpkg.com/lang/en/docs/install/) installed.
cd js; yarn install
You need Protobuf 3. On Linux this might work:
gn gen out/Debug --args='cc_wrapper="ccache" is_debug=true '
``` bash
cd ~
wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip
unzip protoc-3.1.0-linux-x86_64.zip
export PATH=$HOME/bin:$PATH
```
Then build with ninja:
On macOS, using [HomeBrew](https://brew.sh/):
ninja -C out/Debug/ deno
``` bash
brew install protobuf
```
Then you need [protoc-gen-go](https://github.com/golang/protobuf/tree/master/protoc-gen-go) and [go-bindata](https://github.com/jteeuwen/go-bindata):
``` bash
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u github.com/jteeuwen/go-bindata/...
```
You need to get and build [v8worker2](https://github.com/ry/v8worker2). __The package will not build with `go
get` and will log out an error ⚠__
```bash
# pkg-config --cflags v8.pc
Failed to open 'v8.pc': No such file or directory
No package 'v8.pc' found
pkg-config: exit status 1
```
__which can be ignored__. It takes about 30 minutes to build:
``` bash
go get -u github.com/ry/v8worker2
cd $GOPATH/src/github.com/ry/v8worker2
./build.py --use_ccache
```
Maybe also run `git submodule update --init` in the `v8worker2/` dir.
Finally, you can get `deno` and its other Go deps.
``` bash
go get -u github.com/ry/deno/...
```
Now you can build deno and run it:
``` bash
cd $GOPATH/src/github.com/ry/deno
make # Wait for redacted
./deno testdata/001_hello.js # Output: Hello World
```
## `make` commands
``` bash
make deno # Builds the deno executable.
make test # Runs the tests.
make fmt # Formats the code.
make clean # Cleans the build.
```
Other useful commands:
gn args out/Debug/ --list # List build args
gn args out/Debug/ # Modify args in $EDITOR
gn desc out/Debug/ :deno

View file

@ -1,13 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package main
import (
"github.com/ry/deno"
)
func main() {
deno.Init()
deno.Eval("deno_main.js", "denoMain()")
deno.Loop()
}

View file

@ -1,124 +0,0 @@
const print = V8Worker2.print;
// tslint:disable-next-line:no-any
type ConsoleContext = Set<any>;
// tslint:disable-next-line:no-any
function getClassInstanceName(instance: any): string {
if (typeof instance !== "object") {
return "";
}
if (instance && instance.__proto__ && instance.__proto__.constructor) {
return instance.__proto__.constructor.name; // could be "Object" or "Array"
}
return "";
}
// tslint:disable-next-line:no-any
function stringify(ctx: ConsoleContext, value: any): string {
switch (typeof value) {
case "string":
return value;
case "number":
case "boolean":
case "undefined":
case "symbol":
return String(value);
case "function":
if (value.name && value.name !== "anonymous") {
// from MDN spec
return `[Function: ${value.name}]`;
}
return "[Function]";
case "object":
if (value === null) {
return "null";
}
if (ctx.has(value)) {
return "[Circular]";
}
ctx.add(value);
const entries: string[] = [];
if (Array.isArray(value)) {
for (const el of value) {
entries.push(stringify(ctx, el));
}
ctx.delete(value);
if (entries.length === 0) {
return "[]";
}
return `[ ${entries.join(", ")} ]`;
} else {
let baseString = "";
const className = getClassInstanceName(value);
let shouldShowClassName = false;
if (className && className !== "Object" && className !== "anonymous") {
shouldShowClassName = true;
}
for (const key of Object.keys(value)) {
entries.push(`${key}: ${stringify(ctx, value[key])}`);
}
ctx.delete(value);
if (entries.length === 0) {
baseString = "{}";
} else {
baseString = `{ ${entries.join(", ")} }`;
}
if (shouldShowClassName) {
baseString = `${className} ${baseString}`;
}
return baseString;
}
default:
return "[Not Implemented]";
}
}
// tslint:disable-next-line:no-any
function stringifyArgs(args: any[]): string {
const out: string[] = [];
for (const a of args) {
if (typeof a === "string") {
out.push(a);
} else {
// tslint:disable-next-line:no-any
out.push(stringify(new Set<any>(), a));
}
}
return out.join(" ");
}
export class Console {
// tslint:disable-next-line:no-any
log(...args: any[]): void {
print(stringifyArgs(args));
}
debug = this.log;
info = this.log;
// tslint:disable-next-line:no-any
warn(...args: any[]): void {
print(`ERROR: ${stringifyArgs(args)}`);
}
error = this.warn;
// tslint:disable-next-line:no-any
assert(condition: boolean, ...args: any[]): void {
if (!condition) {
throw new Error(`Assertion failed: ${stringifyArgs(args)}`);
}
}
}

14
deno.d.ts vendored
View file

@ -1,14 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
declare module "deno" {
type MessageCallback = (msg: Uint8Array) => void;
function sub(channel: string, cb: MessageCallback): void;
function pub(channel: string, payload: Uint8Array): null | ArrayBuffer;
function readFileSync(filename: string): Uint8Array;
function writeFileSync(
filename: string,
data: Uint8Array,
perm: number
): void;
}

View file

@ -1,6 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// Public deno module.
// TODO get rid of deno.d.ts
export { pub, sub } from "./dispatch";
export { readFileSync, writeFileSync } from "./os";

View file

@ -1,68 +0,0 @@
# Deno Prototype 2
## Status
This code is a rewrite of the privileged parts of Deno. It will soon become
the root of the project.
There are several goals:
* Use the gn build system for fast builds, sane configuration, and easy
linking into Chrome.
* Use V8 snapshots to improve startup time.
* Remove Golang. Although it has been working nicely, I am concerned the
double GC will become a problem sometime down the road.
* Distribute a C++ library called libdeno, containing the snapshotted
typescript runtime.
* Test the message passing and other functionality at that layer before
involving higher level languages.
The contenders for building the privileged part of Deno are Rust and C++.
Thanks to Chrome and gn, using C++ to link into high level libraries is not
untenable. However, there's a lot of interest in Rust in the JS community and
it seems like a reasonable choice. TBD.
There are many people exploring the project, so care will be taken to keep the
original code functional while this is developed. However, once it's ready
the code in this deno2/ directory will be moved to the root.
## Prerequisites
Get Depot Tools and make sure it's in your path.
http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
For linux you need these prereqs:
sudo apt-get install libgtk-3-dev pkg-config ccache
## Build
First install the javascript deps.
cd js; yarn install
TODO(ry) Remove the above step by a deps submodule.
Wrapper around the gclient/gn/ninja for end users. Try this first:
./tools/build.py --use_ccache --debug
If that doesn't work, or you need more control, try calling gn manually:
gn gen out/Debug --args='cc_wrapper="ccache" is_debug=true '
Then build with ninja:
ninja -C out/Debug/ deno
Other useful commands:
gn args out/Debug/ --list # List build args
gn args out/Debug/ # Modify args in $EDITOR

View file

@ -1 +0,0 @@
../msg.proto

102
deno2/msg.proto Normal file
View file

@ -0,0 +1,102 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
syntax = "proto3";
package deno;
option go_package = "deno";
message BaseMsg {
string channel = 1;
bytes payload = 2;
}
message Msg {
enum Command {
ERROR = 0;
START = 1;
CODE_FETCH = 2;
CODE_FETCH_RES = 3;
CODE_CACHE = 4;
EXIT = 5;
TIMER_START = 6;
TIMER_READY = 7;
TIMER_CLEAR = 8;
FETCH_REQ = 9;
FETCH_RES = 10;
READ_FILE_SYNC = 11;
READ_FILE_SYNC_RES = 12;
WRITE_FILE_SYNC = 13;
}
Command command = 1;
// We avoid creating a message for each command (and use oneof or any types)
// In order to reduce code in the size of the generated javascript
// "msg.pb.js". It seems that each new message adds 20k and we want to
// potentially add many hundreds of commands. Therefore we just prefix command
// arguments by their name.
// ERROR
string error = 2;
// START
string start_cwd = 10;
repeated string start_argv = 11;
bool start_debug_flag = 12;
string start_main_js = 13; // The contents of dist/main.js
string start_main_map = 14; // The contents of dist/main.map
// CODE_FETCH
string code_fetch_module_specifier = 20;
string code_fetch_containing_file = 21;
// CODE_FETCH_RES
// If it's a non-http module, moduleName and filename will be the same.
// For http modules, moduleName is its resolved http URL, and filename
// is the location of the locally downloaded source code.
string code_fetch_res_module_name = 30;
string code_fetch_res_filename = 31;
string code_fetch_res_source_code = 32;
string code_fetch_res_output_code = 33; // Non-empty only if cached.
// CODE_CACHE
string code_cache_filename = 41;
string code_cache_source_code = 42;
string code_cache_output_code = 43;
// EXIT
int32 exit_code = 50;
// TIMER_START
int32 timer_start_id = 60;
bool timer_start_interval = 61;
int32 timer_start_delay = 62; // In milliseconds.
// TIMER_READY
int32 timer_ready_id = 70;
bool timer_ready_done = 71;
// TIMER_CLEAR
int32 timer_clear_id = 80;
// FETCH_REQ
int32 fetch_req_id = 90;
string fetch_req_url = 91;
// repeated string fetch_req_header_line = 91
// FETCH_RES
int32 fetch_res_id = 100;
int32 fetch_res_status = 101;
repeated string fetch_res_header_line = 102;
bytes fetch_res_body = 103;
// READ_FILE_SYNC
string read_file_sync_filename = 110;
// READ_FILE_SYNC_RES
bytes read_file_sync_data = 120;
// WRITE_FILE_SYNC
string write_file_sync_filename = 130;
bytes write_file_sync_data = 131;
uint32 write_file_sync_perm = 132;
// write_file_sync_perm specified by https://godoc.org/os#FileMode
}

View file

@ -1,67 +0,0 @@
#!/usr/bin/env python
# Get Depot Tools and make sure it's in your path.
# http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up
# Use .gclient to modify the deps.
import os
import sys
import subprocess
import argparse
TARGET = "deno"
parser = argparse.ArgumentParser(description="build.py")
parser.add_argument('--debug', dest='debug', action='store_true')
parser.add_argument('--use_ccache', dest='use_ccache', action='store_true')
parser.add_argument('--sync', dest='sync', action='store_true')
parser.set_defaults(debug=False, use_ccache=False, sync=False)
args = parser.parse_args()
root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
def main():
os.chdir(root_path)
buildName = "Debug" if args.debug else "Default"
buildDir = os.path.join(root_path, "out", buildName)
# Run sync if any of the dep dirs don't exist.
# Or the user supplied the --sync flag.
if args.sync or dirsMissing():
run(["gclient", "sync", "--no-history"])
# Run gn gen out/Default if out doesn't exist.
if not os.path.exists(buildDir):
gn_gen = ["gn", "gen", buildDir]
gn_args = []
if args.debug:
gn_args.append("is_debug=true")
if args.use_ccache:
gn_args.append("cc_wrapper=\"ccache\"")
if len(gn_args) > 0:
gn_gen += ["--args=%s" % " ".join(gn_args)]
run(gn_gen)
# Always run ninja.
run(["ninja", "-C", buildDir, TARGET])
def run(args):
print " ".join(args)
env = os.environ.copy()
subprocess.check_call(args, env=env)
def dirsMissing():
dirsToLoad = [
"v8",
"third_party/protobuf",
"tools/protoc_wrapper",
"third_party/zlib",
]
for d in dirsToLoad:
if not os.path.exists(d):
return True
return False
if '__main__' == __name__:
main()

View file

@ -1,111 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"crypto/md5"
"encoding/hex"
"flag"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"runtime"
"strings"
)
var flagCacheDir = flag.String("cachedir", "",
"Where to cache compilation artifacts. Default: ~/.deno")
var DenoDir string
var CacheDir string
var SrcDir string
func SourceCodeHash(filename string, sourceCodeBuf []byte) string {
h := md5.New()
h.Write([]byte(filename))
h.Write(sourceCodeBuf)
return hex.EncodeToString(h.Sum(nil))
}
func CacheFileName(filename string, sourceCodeBuf []byte) string {
cacheKey := SourceCodeHash(filename, sourceCodeBuf)
return path.Join(CacheDir, cacheKey+".js")
}
// Fetches a remoteUrl but also caches it to the localFilename.
func FetchRemoteSource(remoteUrl string, localFilename string) ([]byte, error) {
logDebug("FetchRemoteSource %s %s", remoteUrl, localFilename)
assert(strings.HasPrefix(localFilename, SrcDir),
"Expected filename to start with SrcDir: "+localFilename)
var sourceReader io.Reader
file, err := os.Open(localFilename)
if *flagReload || os.IsNotExist(err) {
// Fetch from HTTP.
println("Downloading", remoteUrl)
res, err := http.Get(remoteUrl)
if err != nil {
return nil, err
}
defer res.Body.Close()
err = os.MkdirAll(path.Dir(localFilename), 0700)
if err != nil {
return nil, err
}
// Write to local file. Need to reopen it for writing.
file, err = os.OpenFile(localFilename, os.O_RDWR|os.O_CREATE, 0700)
if err != nil {
return nil, err
}
sourceReader = io.TeeReader(res.Body, file) // Fancy!
} else if err != nil {
return nil, err
} else {
sourceReader = file
}
defer file.Close()
return ioutil.ReadAll(sourceReader)
}
func LoadOutputCodeCache(filename string, sourceCodeBuf []byte) (
outputCode string, err error) {
cacheFn := CacheFileName(filename, sourceCodeBuf)
outputCodeBuf, err := ioutil.ReadFile(cacheFn)
if os.IsNotExist(err) {
// Ignore error if we can't find the cache file.
err = nil
} else if err == nil {
outputCode = string(outputCodeBuf)
}
return outputCode, err
}
func UserHomeDir() string {
if runtime.GOOS == "windows" {
home := path.Join(os.Getenv("HOMEDRIVE"), os.Getenv("HOMEPATH"))
if home == "" {
home = os.Getenv("USERPROFILE")
}
return home
}
return os.Getenv("HOME")
}
func createDirs() {
if *flagCacheDir == "" {
DenoDir = path.Join(UserHomeDir(), ".deno")
} else {
DenoDir = *flagCacheDir
}
CacheDir = path.Join(DenoDir, "cache")
err := os.MkdirAll(CacheDir, 0700)
check(err)
SrcDir = path.Join(DenoDir, "src")
err = os.MkdirAll(SrcDir, 0700)
check(err)
}

View file

@ -1,46 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"io/ioutil"
"testing"
)
func SetCacheDirForTest(prefix string) {
dir, err := ioutil.TempDir("", prefix)
check(err)
CacheDir = dir
}
func TestLoadOutputCodeCache(t *testing.T) {
SetCacheDirForTest("TestLoadOutputCodeCache")
filename := "Hello.ts"
sourceCodeBuf := []byte("1+2")
cacheFn := CacheFileName(filename, sourceCodeBuf)
outputCode, err := LoadOutputCodeCache(filename, sourceCodeBuf)
if err != nil {
t.Fatalf(err.Error())
}
if outputCode != "" {
t.Fatalf("Expected empty outputCode but got <<%s>>", outputCode)
}
// Now let's write to the cache file
err = ioutil.WriteFile(cacheFn, []byte("blah"), 0700)
if err != nil {
t.Fatalf(err.Error())
}
// Try it again.
outputCode, err = LoadOutputCodeCache(filename, sourceCodeBuf)
if err != nil {
t.Fatalf(err.Error())
}
if outputCode != "blah" {
t.Fatalf("Bad outputCode but got <<%s>>", outputCode)
}
}

View file

@ -1,126 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"github.com/golang/protobuf/proto"
"sync"
)
var resChan = make(chan *BaseMsg, 10)
var doneChan = make(chan bool)
var wg sync.WaitGroup
var stats struct {
v8workerSend int
v8workerRespond int
v8workerRecv int
v8workerBytesSent int
v8workerBytesRecv int
}
var channels = make(map[string][]Subscriber)
type Subscriber func(payload []byte) []byte
func recv(buf []byte) (response []byte) {
stats.v8workerRecv++
stats.v8workerBytesRecv += len(buf)
msg := &BaseMsg{}
check(proto.Unmarshal(buf, msg))
assert(len(msg.Payload) > 0, "BaseMsg has empty payload.")
subscribers, ok := channels[msg.Channel]
if !ok {
panic("No subscribers for channel " + msg.Channel)
}
for i := 0; i < len(subscribers); i++ {
s := subscribers[i]
r := s(msg.Payload)
if r != nil {
response = r
}
}
if response != nil {
stats.v8workerRespond++
stats.v8workerBytesSent += len(response)
}
return response
}
func Sub(channel string, cb Subscriber) {
subscribers, ok := channels[channel]
if !ok {
subscribers = make([]Subscriber, 0)
}
subscribers = append(subscribers, cb)
channels[channel] = subscribers
}
func Pub(channel string, payload []byte) {
wg.Add(1)
resChan <- &BaseMsg{
Channel: channel,
Payload: payload,
}
}
func PubMsg(channel string, msg *Msg) {
payload, err := proto.Marshal(msg)
check(err)
Pub(channel, payload)
}
func DispatchLoop() {
wg.Add(1)
first := true
// In a goroutine, we wait on for all goroutines to complete (for example
// timers). We use this to signal to the main thread to exit.
// wg.Add(1) basically translates to uv_ref, if this was Node.
// wg.Done() basically translates to uv_unref
go func() {
wg.Wait()
doneChan <- true
}()
for {
select {
case msg := <-resChan:
out, err := proto.Marshal(msg)
check(err)
err = worker.SendBytes(out)
stats.v8workerSend++
stats.v8workerBytesSent += len(out)
exitOnError(err)
wg.Done() // Corresponds to the wg.Add(1) in Pub().
case <-doneChan:
// All goroutines have completed. Now we can exit main().
checkChanEmpty()
return
}
// We don't want to exit until we've received at least one message.
// This is so the program doesn't exit after sending the "start"
// message.
if first {
wg.Done()
}
first = false
}
}
func checkChanEmpty() {
// We've received a done event. As a sanity check, make sure that resChan is
// empty.
select {
case _, ok := <-resChan:
if ok {
panic("Read a message from resChan after doneChan closed.")
} else {
panic("resChan closed. Unexpected.")
}
default:
// No value ready, moving on.
}
}

View file

@ -1,73 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import { typedArrayToArrayBuffer } from "./util";
import { _global } from "./globals";
import { deno as pb } from "./msg.pb";
export type MessageCallback = (msg: Uint8Array) => void;
//type MessageStructCallback = (msg: pb.IMsg) => void;
const send = V8Worker2.send;
const channels = new Map<string, MessageCallback[]>();
export function sub(channel: string, cb: MessageCallback): void {
let subscribers = channels.get(channel);
if (!subscribers) {
subscribers = [];
channels.set(channel, subscribers);
}
subscribers.push(cb);
}
/*
export function subMsg(channel: string, cb: MessageStructCallback): void {
sub(channel, (payload: Uint8Array) => {
const msg = pb.Msg.decode(payload);
if (msg.error != null) {
f.onError(new Error(msg.error));
} else {
cb(msg);
}
});
}
*/
export function pub(channel: string, payload: Uint8Array): null | ArrayBuffer {
const msg = pb.BaseMsg.fromObject({ channel, payload });
const ui8 = pb.BaseMsg.encode(msg).finish();
const ab = typedArrayToArrayBuffer(ui8);
return send(ab);
}
// Internal version of "pub".
// TODO add internal version of "sub"
export function pubInternal(channel: string, obj: pb.IMsg): null | pb.Msg {
const msg = pb.Msg.fromObject(obj);
const ui8 = pb.Msg.encode(msg).finish();
const resBuf = pub(channel, ui8);
if (resBuf != null && resBuf.byteLength > 0) {
const res = pb.Msg.decode(new Uint8Array(resBuf));
if (res != null && res.error != null && res.error.length > 0) {
throw Error(res.error);
}
return res;
} else {
return null;
}
}
V8Worker2.recv((ab: ArrayBuffer) => {
const msg = pb.BaseMsg.decode(new Uint8Array(ab));
const subscribers = channels.get(msg.channel);
if (subscribers == null) {
throw Error(`No subscribers for channel "${msg.channel}".`);
}
for (const subscriber of subscribers) {
subscriber(msg.payload);
}
});
// Delete the V8Worker2 from the global object, so that no one else can receive
// messages.
_global["V8Worker2"] = null;

11
echo.go
View file

@ -1,11 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
// For testing
func InitEcho() {
Sub("echo", func(buf []byte) []byte {
Pub("echo", buf)
return nil
})
}

View file

@ -1,72 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"github.com/golang/protobuf/proto"
"io/ioutil"
"net/http"
)
func InitFetch() {
Sub("fetch", func(buf []byte) []byte {
msg := &Msg{}
check(proto.Unmarshal(buf, msg))
switch msg.Command {
case Msg_FETCH_REQ:
return Fetch(
msg.FetchReqId,
msg.FetchReqUrl)
default:
panic("[fetch] Unexpected message " + string(buf))
}
})
}
func Fetch(id int32, targetUrl string) []byte {
logDebug("Fetch %d %s", id, targetUrl)
async(func() {
resMsg := &Msg{
Command: Msg_FETCH_RES,
FetchResId: id,
}
if !Perms.Net {
resMsg.Error = "Network access denied."
PubMsg("fetch", resMsg)
return
}
resp, err := http.Get(targetUrl)
if err != nil {
resMsg.Error = err.Error()
PubMsg("fetch", resMsg)
return
}
if resp == nil {
resMsg.Error = "resp is nil "
PubMsg("fetch", resMsg)
return
}
resMsg.FetchResStatus = int32(resp.StatusCode)
logDebug("fetch success %d %s", resMsg.FetchResStatus, targetUrl)
PubMsg("fetch", resMsg)
// Now we read the body and send another message0
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if resp == nil {
resMsg.Error = "resp is nil "
PubMsg("fetch", resMsg)
return
}
resMsg.FetchResBody = body
PubMsg("fetch", resMsg)
// TODO streaming.
})
return nil
}

146
fetch.ts
View file

@ -1,146 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import {
assert,
log,
createResolvable,
Resolvable,
typedArrayToArrayBuffer
} from "./util";
import { pubInternal, sub } from "./dispatch";
import { deno as pb } from "./msg.pb";
export function initFetch() {
sub("fetch", (payload: Uint8Array) => {
const msg = pb.Msg.decode(payload);
assert(msg.command === pb.Msg.Command.FETCH_RES);
const id = msg.fetchResId;
const f = fetchRequests.get(id);
assert(f != null, `Couldn't find FetchRequest id ${id}`);
f.onMsg(msg);
});
}
const fetchRequests = new Map<number, FetchRequest>();
class FetchResponse implements Response {
readonly url: string;
body: null;
bodyUsed = false; // TODO
status: number;
statusText = "FIXME"; // TODO
readonly type = "basic"; // TODO
redirected = false; // TODO
headers: null; // TODO
//private bodyChunks: Uint8Array[] = [];
private first = true;
constructor(readonly req: FetchRequest) {
this.url = req.url;
}
bodyWaiter: Resolvable<ArrayBuffer>;
arrayBuffer(): Promise<ArrayBuffer> {
this.bodyWaiter = createResolvable();
return this.bodyWaiter;
}
blob(): Promise<Blob> {
throw Error("not implemented");
}
formData(): Promise<FormData> {
throw Error("not implemented");
}
async json(): Promise<object> {
const text = await this.text();
return JSON.parse(text);
}
async text(): Promise<string> {
const ab = await this.arrayBuffer();
const decoder = new TextDecoder("utf-8");
return decoder.decode(ab);
}
get ok(): boolean {
return 200 <= this.status && this.status < 300;
}
clone(): Response {
throw Error("not implemented");
}
onHeader: (res: Response) => void;
onError: (error: Error) => void;
onMsg(msg: pb.Msg) {
if (msg.error !== null && msg.error !== "") {
//throw new Error(msg.error)
this.onError(new Error(msg.error));
return;
}
if (this.first) {
this.first = false;
this.status = msg.fetchResStatus;
this.onHeader(this);
} else {
// Body message. Assuming it all comes in one message now.
const ab = typedArrayToArrayBuffer(msg.fetchResBody);
this.bodyWaiter.resolve(ab);
}
}
}
let nextFetchId = 0;
//TODO implements Request
class FetchRequest {
private readonly id: number;
response: FetchResponse;
constructor(readonly url: string) {
this.id = nextFetchId++;
fetchRequests.set(this.id, this);
this.response = new FetchResponse(this);
}
onMsg(msg: pb.Msg) {
this.response.onMsg(msg);
}
destroy() {
fetchRequests.delete(this.id);
}
start() {
log("dispatch FETCH_REQ", this.id, this.url);
const res = pubInternal("fetch", {
command: pb.Msg.Command.FETCH_REQ,
fetchReqId: this.id,
fetchReqUrl: this.url
});
assert(res == null);
}
}
export function fetch(
input?: Request | string,
init?: RequestInit
): Promise<Response> {
const fetchReq = new FetchRequest(input as string);
const response = fetchReq.response;
return new Promise((resolve, reject) => {
// tslint:disable-next-line:no-any
response.onHeader = (response: any) => {
log("onHeader");
resolve(response);
};
response.onError = (error: Error) => {
log("onError", error);
reject(error);
};
fetchReq.start();
});
}

View file

@ -1,32 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import * as timer from "./timers";
// If you use the eval function indirectly, by invoking it via a reference
// other than eval, as of ECMAScript 5 it works in the global scope rather than
// the local scope. This means, for instance, that function declarations create
// global functions, and that the code being evaluated doesn't have access to
// local variables within the scope where it's being called.
export const globalEval = eval;
// A reference to the global object.
// TODO The underscore is because it's conflicting with @types/node.
export const _global = globalEval("this");
_global["window"] = _global; // Create a window object.
import "./url";
_global["setTimeout"] = timer.setTimeout;
_global["setInterval"] = timer.setInterval;
_global["clearTimeout"] = timer.clearTimer;
_global["clearInterval"] = timer.clearTimer;
import { Console } from "./console";
_global["console"] = new Console();
import { fetch } from "./fetch";
_global["fetch"] = fetch;
import { TextEncoder, TextDecoder } from "text-encoding";
_global["TextEncoder"] = TextEncoder;
_global["TextDecoder"] = TextDecoder;

View file

@ -1,139 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"bytes"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"path"
"strings"
"testing"
)
var denoFn string
// Some tests require an HTTP server. We start one here.
// Note that "localhost:4545" is hardcoded into the tests at the moment,
// so if the server runs on a different port, it will fail.
func startServer() {
l, err := net.Listen("tcp", ":4545")
check(err)
rootHandler := http.FileServer(http.Dir("."))
go func() {
err := http.Serve(l, rootHandler)
check(err)
}()
}
func listTestFiles() []string {
files, err := ioutil.ReadDir("testdata")
check(err)
out := make([]string, 0)
for _, file := range files {
fn := file.Name()
if strings.HasSuffix(fn, ".out") {
out = append(out, fn)
}
}
return out
}
func checkOutput(t *testing.T, outFile string, shouldSucceed bool) {
outFile = path.Join("testdata", outFile)
jsFile := strings.TrimSuffix(outFile, ".out")
expected, err := ioutil.ReadFile(outFile)
if err != nil {
t.Fatal(err.Error())
}
actual, _, err := deno(jsFile)
if shouldSucceed && err != nil {
t.Fatalf("Expected success %s", err.Error())
} else if !shouldSucceed && err == nil {
t.Fatalf("Expected failure but got success")
}
if !patternMatch(string(expected), string(actual)) {
t.Fatalf(`Actual output does not match expected.
-----Actual-------------------
%s-----Expected-----------------
%s------------------------------`, string(actual), string(expected))
}
}
func deno(inputFn string) (actual []byte, cachedir string, err error) {
cachedir, err = ioutil.TempDir("", "TestIntegration")
check(err)
cmd := exec.Command(denoFn, "--cachedir="+cachedir, inputFn)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err = cmd.Run()
actual = out.Bytes()
return
}
func integrationTestSetup() {
if denoFn == "" {
startServer()
cwd, err := os.Getwd()
check(err)
denoFn = path.Join(cwd, "deno")
}
}
func TestIntegrationFiles(t *testing.T) {
integrationTestSetup()
outFiles := listTestFiles()
for _, outFile := range outFiles {
t.Run(outFile, func(t *testing.T) {
shouldSucceed := strings.Index(outFile, "error") < 0
checkOutput(t, outFile, shouldSucceed)
})
}
}
func TestIntegrationUrlArgs(t *testing.T) {
integrationTestSetup()
// Using good port 4545
_, cachedir, err := deno("http://localhost:4545/testdata/001_hello.js")
if err != nil {
t.Fatalf("Expected success. %s", err.Error())
}
cacheFn := path.Join(cachedir, "src/localhost:4545/testdata/001_hello.js")
println("good cacheFn", cacheFn)
if !exists(cacheFn) {
t.Fatalf("Expected 200 at '%s'", cacheFn)
}
// TODO check output
// Using bad port 4546 instead of 4545.
_, cachedir, err = deno("http://localhost:4546/testdata/001_hello.js")
if err == nil {
t.Fatalf("Expected 404. %s", err.Error())
}
// Check that cache dir is empty.
cacheFn = path.Join(cachedir, "src/localhost:4546/testdata/001_hello.js")
println("bad cacheFn", cacheFn)
if exists(cacheFn) {
t.Fatalf("Expected 404 at '%s'", cacheFn)
}
}
func TestTestsTs(t *testing.T) {
integrationTestSetup()
// TODO Need unit test for each of the permissions.
cmd := exec.Command(denoFn, "--allow-net", "--allow-write", "tests.ts")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
t.Fatal(err.Error())
}
}

141
main.go
View file

@ -1,141 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"flag"
"fmt"
"os"
"path"
"runtime/pprof"
"github.com/ry/v8worker2"
)
var flagReload = flag.Bool("reload", false, "Reload cached remote source code.")
var flagV8Options = flag.Bool("v8-options", false, "Print V8 command line options.")
var flagDebug = flag.Bool("debug", false, "Enable debug output.")
var flagCPUProf = flag.String("cpuprof", "", "Write golang cpu profile to file.")
var flagMemProf = flag.String("memprof", "", "Write golang memory profile to file.")
var flagAllowRead = flag.Bool("allow-read", true,
"Allow program to read file system.")
var flagAllowWrite = flag.Bool("allow-write", false,
"Allow program to write to the fs.")
var flagAllowNet = flag.Bool("allow-net", false,
"Allow program to make network connection.")
var memProfile *os.File
var Perms struct {
FsRead bool
FsWrite bool
Net bool
}
func setPerms() {
Perms.FsRead = *flagAllowRead
Perms.FsWrite = *flagAllowWrite
Perms.Net = *flagAllowNet
}
func stringAsset(filename string) string {
data, err := Asset(path.Join("dist", filename))
check(err)
return string(data)
}
func FlagsParse() []string {
flag.Parse()
args := flag.Args()
setPerms()
if *flagV8Options {
args = append(args, "--help")
}
// Adding this causes testdata/007_stack_trace.ts to fail without a
// stacktrace.
// args = append(args, "--abort-on-uncaught-exception")
args = v8worker2.SetFlags(args)
return args
}
// There is a single global worker for this process.
// This file should be the only part of deno that directly access it, so that
// all interaction with V8 can go through a single point.
var worker *v8worker2.Worker
var workerArgs []string
var main_js string
var main_map string
func Init() {
workerArgs = FlagsParse()
if len(workerArgs) == 0 {
fmt.Fprintf(os.Stderr, "Usage: %s file.ts\n", os.Args[0])
flag.PrintDefaults()
os.Exit(1)
}
// Maybe start Golang profilers.
// Use --prof for profiling JS.
StartProfiling()
createDirs()
InitOS()
InitEcho()
InitTimers()
InitFetch()
worker = v8worker2.New(recv)
main_js = stringAsset("main.js")
err := worker.Load("/main.js", main_js)
exitOnError(err)
main_map = stringAsset("main.map")
}
func StartProfiling() {
if *flagCPUProf != "" {
cpuProfile, err := os.Create(*flagCPUProf)
check(err)
check(pprof.StartCPUProfile(cpuProfile))
}
if *flagMemProf != "" {
var err error
memProfile, err = os.Create(*flagMemProf)
check(err)
check(pprof.WriteHeapProfile(memProfile))
}
}
func stopProfiling() {
if *flagCPUProf != "" {
pprof.StopCPUProfile()
}
if *flagMemProf != "" {
check(memProfile.Close())
}
}
// It's up to library users to call
// deno.Eval("deno_main.js", "denoMain()")
func Eval(filename string, code string) {
err := worker.Load(filename, code)
exitOnError(err)
}
func Loop() {
cwd, err := os.Getwd()
check(err)
PubMsg("start", &Msg{
Command: Msg_START,
StartCwd: cwd,
StartArgv: workerArgs,
StartDebugFlag: *flagDebug,
StartMainJs: main_js,
StartMainMap: main_map,
})
DispatchLoop()
stopProfiling()
}

54
main.ts
View file

@ -1,54 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// This allows us to have async/await in our code. It must be loaded first.
import "babel-polyfill";
import * as dispatch from "./dispatch";
import { deno as pb } from "./msg.pb";
import * as runtime from "./runtime";
import * as util from "./util";
import { initTimers } from "./timers";
import { initFetch } from "./fetch";
// To control internal logging output
// Set with the -debug command-line flag.
export let debug = false;
let startCalled = false;
// denoMain is needed to allow hooks into the system.
// Also eventual snapshot support needs it.
// tslint:disable-next-line:no-any
(window as any)["denoMain"] = () => {
// tslint:disable-next-line:no-any
delete (window as any)["denoMain"];
initTimers();
initFetch();
dispatch.sub("start", (payload: Uint8Array) => {
if (startCalled) {
throw Error("start message received more than once!");
}
startCalled = true;
const msg = pb.Msg.decode(payload);
const {
startCwd: cwd,
startArgv: argv,
startDebugFlag: debugFlag,
startMainJs: mainJs,
startMainMap: mainMap
} = msg;
debug = debugFlag;
util.log("start", { cwd, argv, debugFlag });
runtime.setup(mainJs, mainMap);
const inputFn = argv[0];
const mod = runtime.resolveModule(inputFn, `${cwd}/`);
mod.compileAndRun();
});
};

102
msg.proto
View file

@ -1,102 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
syntax = "proto3";
package deno;
option go_package = "deno";
message BaseMsg {
string channel = 1;
bytes payload = 2;
}
message Msg {
enum Command {
ERROR = 0;
START = 1;
CODE_FETCH = 2;
CODE_FETCH_RES = 3;
CODE_CACHE = 4;
EXIT = 5;
TIMER_START = 6;
TIMER_READY = 7;
TIMER_CLEAR = 8;
FETCH_REQ = 9;
FETCH_RES = 10;
READ_FILE_SYNC = 11;
READ_FILE_SYNC_RES = 12;
WRITE_FILE_SYNC = 13;
}
Command command = 1;
// We avoid creating a message for each command (and use oneof or any types)
// In order to reduce code in the size of the generated javascript
// "msg.pb.js". It seems that each new message adds 20k and we want to
// potentially add many hundreds of commands. Therefore we just prefix command
// arguments by their name.
// ERROR
string error = 2;
// START
string start_cwd = 10;
repeated string start_argv = 11;
bool start_debug_flag = 12;
string start_main_js = 13; // The contents of dist/main.js
string start_main_map = 14; // The contents of dist/main.map
// CODE_FETCH
string code_fetch_module_specifier = 20;
string code_fetch_containing_file = 21;
// CODE_FETCH_RES
// If it's a non-http module, moduleName and filename will be the same.
// For http modules, moduleName is its resolved http URL, and filename
// is the location of the locally downloaded source code.
string code_fetch_res_module_name = 30;
string code_fetch_res_filename = 31;
string code_fetch_res_source_code = 32;
string code_fetch_res_output_code = 33; // Non-empty only if cached.
// CODE_CACHE
string code_cache_filename = 41;
string code_cache_source_code = 42;
string code_cache_output_code = 43;
// EXIT
int32 exit_code = 50;
// TIMER_START
int32 timer_start_id = 60;
bool timer_start_interval = 61;
int32 timer_start_delay = 62; // In milliseconds.
// TIMER_READY
int32 timer_ready_id = 70;
bool timer_ready_done = 71;
// TIMER_CLEAR
int32 timer_clear_id = 80;
// FETCH_REQ
int32 fetch_req_id = 90;
string fetch_req_url = 91;
// repeated string fetch_req_header_line = 91
// FETCH_RES
int32 fetch_res_id = 100;
int32 fetch_res_status = 101;
repeated string fetch_res_header_line = 102;
bytes fetch_res_body = 103;
// READ_FILE_SYNC
string read_file_sync_filename = 110;
// READ_FILE_SYNC_RES
bytes read_file_sync_data = 120;
// WRITE_FILE_SYNC
string write_file_sync_filename = 130;
bytes write_file_sync_data = 131;
uint32 write_file_sync_perm = 132;
// write_file_sync_perm specified by https://godoc.org/os#FileMode
}

195
os.go
View file

@ -1,195 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"github.com/golang/protobuf/proto"
"github.com/spf13/afero"
"io/ioutil"
"net/url"
"os"
"path"
"strings"
)
const assetPrefix string = "/$asset$/"
var fs afero.Fs
func InitOS() {
if Perms.FsWrite {
assert(Perms.FsRead, "Write access requires read access.")
fs = afero.NewOsFs()
} else if Perms.FsRead {
fs = afero.NewReadOnlyFs(afero.NewOsFs())
} else {
panic("Not implemented.")
}
Sub("os", func(buf []byte) []byte {
msg := &Msg{}
check(proto.Unmarshal(buf, msg))
switch msg.Command {
case Msg_CODE_FETCH:
return HandleCodeFetch(
msg.CodeFetchModuleSpecifier,
msg.CodeFetchContainingFile)
case Msg_CODE_CACHE:
return HandleCodeCache(
msg.CodeCacheFilename,
msg.CodeCacheSourceCode,
msg.CodeCacheOutputCode)
case Msg_EXIT:
os.Exit(int(msg.ExitCode))
case Msg_READ_FILE_SYNC:
return ReadFileSync(msg.ReadFileSyncFilename)
case Msg_WRITE_FILE_SYNC:
return WriteFileSync(msg.WriteFileSyncFilename, msg.WriteFileSyncData,
msg.WriteFileSyncPerm)
default:
panic("[os] Unexpected message " + string(buf))
}
return nil
})
}
func SrcFileToUrl(filename string) string {
assert(len(SrcDir) > 0, "SrcDir shouldn't be empty")
if strings.HasPrefix(filename, SrcDir) {
rest := strings.TrimPrefix(filename, SrcDir)
if rest[0] == '/' {
rest = rest[1:]
}
return "http://" + rest
} else {
return filename
}
}
func ResolveModule(moduleSpecifier string, containingFile string) (
moduleName string, filename string, err error) {
logDebug("os.go ResolveModule moduleSpecifier %s containingFile %s",
moduleSpecifier, containingFile)
containingFile = SrcFileToUrl(containingFile)
moduleSpecifier = SrcFileToUrl(moduleSpecifier)
logDebug("os.go ResolveModule after moduleSpecifier %s containingFile %s",
moduleSpecifier, containingFile)
moduleUrl, err := url.Parse(moduleSpecifier)
if err != nil {
return
}
baseUrl, err := url.Parse(containingFile)
if err != nil {
return
}
resolved := baseUrl.ResolveReference(moduleUrl)
moduleName = resolved.String()
if resolved.IsAbs() {
filename = path.Join(SrcDir, resolved.Host, resolved.Path)
} else {
filename = resolved.Path
}
return
}
func HandleCodeFetch(moduleSpecifier string, containingFile string) (out []byte) {
assert(moduleSpecifier != "", "moduleSpecifier shouldn't be empty")
res := &Msg{}
var sourceCodeBuf []byte
var err error
defer func() {
if err != nil {
res.Error = err.Error()
}
out, err = proto.Marshal(res)
check(err)
}()
moduleName, filename, err := ResolveModule(moduleSpecifier, containingFile)
if err != nil {
return
}
logDebug("CodeFetch moduleName %s moduleSpecifier %s containingFile %s filename %s",
moduleName, moduleSpecifier, containingFile, filename)
if isRemote(moduleName) {
sourceCodeBuf, err = FetchRemoteSource(moduleName, filename)
} else if strings.HasPrefix(moduleName, assetPrefix) {
f := strings.TrimPrefix(moduleName, assetPrefix)
sourceCodeBuf, err = Asset(path.Join("dist", f))
if err != nil {
logDebug("%s Asset doesn't exist. Return without error", moduleName)
err = nil
}
} else {
assert(moduleName == filename,
"if a module isn't remote, it should have the same filename")
sourceCodeBuf, err = ioutil.ReadFile(moduleName)
}
if err != nil {
return
}
outputCode, err := LoadOutputCodeCache(filename, sourceCodeBuf)
if err != nil {
return
}
var sourceCode = string(sourceCodeBuf)
res = &Msg{
Command: Msg_CODE_FETCH_RES,
CodeFetchResModuleName: moduleName,
CodeFetchResFilename: filename,
CodeFetchResSourceCode: sourceCode,
CodeFetchResOutputCode: outputCode,
}
return
}
func HandleCodeCache(filename string, sourceCode string,
outputCode string) []byte {
fn := CacheFileName(filename, []byte(sourceCode))
outputCodeBuf := []byte(outputCode)
err := ioutil.WriteFile(fn, outputCodeBuf, 0600)
res := &Msg{}
if err != nil {
res.Error = err.Error()
}
out, err := proto.Marshal(res)
check(err)
return out
}
func ReadFileSync(filename string) []byte {
data, err := afero.ReadFile(fs, filename)
res := &Msg{
Command: Msg_READ_FILE_SYNC_RES,
}
if err != nil {
res.Error = err.Error()
} else {
res.ReadFileSyncData = data
}
out, err := proto.Marshal(res)
check(err)
return out
}
func WriteFileSync(filename string, data []byte, perm uint32) []byte {
err := afero.WriteFile(fs, filename, data, os.FileMode(perm))
res := &Msg{}
if err != nil {
res.Error = err.Error()
}
out, err := proto.Marshal(res)
check(err)
return out
}

65
os.ts
View file

@ -1,65 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import { ModuleInfo } from "./types";
import { pubInternal } from "./dispatch";
import { deno as pb } from "./msg.pb";
import { assert } from "./util";
export function exit(exitCode = 0): void {
pubInternal("os", {
command: pb.Msg.Command.EXIT,
exitCode
});
}
export function codeFetch(
moduleSpecifier: string,
containingFile: string
): ModuleInfo {
const res = pubInternal("os", {
command: pb.Msg.Command.CODE_FETCH,
codeFetchModuleSpecifier: moduleSpecifier,
codeFetchContainingFile: containingFile
});
assert(res.command === pb.Msg.Command.CODE_FETCH_RES);
return {
moduleName: res.codeFetchResModuleName,
filename: res.codeFetchResFilename,
sourceCode: res.codeFetchResSourceCode,
outputCode: res.codeFetchResOutputCode
};
}
export function codeCache(
filename: string,
sourceCode: string,
outputCode: string
): void {
pubInternal("os", {
command: pb.Msg.Command.CODE_CACHE,
codeCacheFilename: filename,
codeCacheSourceCode: sourceCode,
codeCacheOutputCode: outputCode
});
}
export function readFileSync(filename: string): Uint8Array {
const res = pubInternal("os", {
command: pb.Msg.Command.READ_FILE_SYNC,
readFileSyncFilename: filename
});
return res.readFileSyncData;
}
export function writeFileSync(
filename: string,
data: Uint8Array,
perm: number
): void {
pubInternal("os", {
command: pb.Msg.Command.WRITE_FILE_SYNC,
writeFileSyncFilename: filename,
writeFileSyncData: data,
writeFileSyncPerm: perm
});
}

View file

@ -1,87 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"path"
"testing"
)
func AssertEqual(t *testing.T, actual string, expected string) {
if actual != expected {
t.Fatalf("not equal <<%s>> <<%s>>", actual, expected)
}
}
func TestResolveModule1(t *testing.T) {
createDirs()
moduleName, filename, err := ResolveModule(
"http://localhost:4545/testdata/subdir/print_hello.ts",
"/Users/rld/go/src/github.com/ry/deno/testdata/006_url_imports.ts")
if err != nil {
t.Fatalf(err.Error())
}
AssertEqual(t, moduleName,
"http://localhost:4545/testdata/subdir/print_hello.ts")
AssertEqual(t, filename,
path.Join(SrcDir, "localhost:4545/testdata/subdir/print_hello.ts"))
}
func TestResolveModule2(t *testing.T) {
createDirs()
moduleName, filename, err := ResolveModule(
"./subdir/print_hello.ts",
"/Users/rld/go/src/github.com/ry/deno/testdata/006_url_imports.ts")
if err != nil {
t.Fatalf(err.Error())
}
AssertEqual(t, moduleName,
"/Users/rld/go/src/github.com/ry/deno/testdata/subdir/print_hello.ts")
AssertEqual(t, filename,
"/Users/rld/go/src/github.com/ry/deno/testdata/subdir/print_hello.ts")
}
func TestResolveModule3(t *testing.T) {
createDirs()
// In the case where the containingFile is a directory (indicated with a
// trailing slash)
moduleName, filename, err := ResolveModule(
"testdata/001_hello.js",
"/Users/rld/go/src/github.com/ry/deno/")
if err != nil {
t.Fatalf(err.Error())
}
AssertEqual(t, moduleName,
"/Users/rld/go/src/github.com/ry/deno/testdata/001_hello.js")
AssertEqual(t, filename,
"/Users/rld/go/src/github.com/ry/deno/testdata/001_hello.js")
}
func TestResolveModule4(t *testing.T) {
createDirs()
// Files in SrcDir should resolve to URLs.
moduleName, filename, err := ResolveModule(
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"),
".")
if err != nil {
t.Fatalf(err.Error())
}
AssertEqual(t, moduleName,
"http://unpkg.com/liltest@0.0.5/index.ts")
AssertEqual(t, filename,
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"))
}
func TestResolveModuleExtensionsAintSpecial(t *testing.T) {
createDirs()
moduleName, filename, err := ResolveModule(
"./util",
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/index.ts"))
if err != nil {
t.Fatalf(err.Error())
}
AssertEqual(t, moduleName,
"http://unpkg.com/liltest@0.0.5/util")
AssertEqual(t, filename,
path.Join(SrcDir, "unpkg.com/liltest@0.0.5/util"))
}

View file

@ -1,25 +0,0 @@
{
"name": "deno",
"scripts": {
"lint": "tslint -p tsconfig.json -c tslint.json",
"fmt": "prettier --write *.ts* *.js *.json"
},
"devDependencies": {
"@types/base64-js": "^1.2.5",
"@types/source-map-support": "^0.4.0",
"babel-polyfill": "^6.26.0",
"base64-js": "^1.3.0",
"espree": "^3.5.3",
"jsdoc": "^3.5.5",
"parcel-bundler": "^1.8.1",
"prettier": "^1.12.1",
"protobufjs": "^6.8.6",
"source-map": "0.6.0",
"text-encoding": "^0.6.4",
"tmp": "0.0.33",
"tslint": "5.10.0",
"typescript": "^2.8.3",
"uglify-js": "^2.8.29"
},
"dependencies": {}
}

View file

@ -1,339 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// Glossary
// outputCode = generated javascript code
// sourceCode = typescript code (or input javascript code)
// moduleName = a resolved module name
// fileName = an unresolved raw fileName.
// for http modules , its the path to the locally downloaded
// version.
import * as ts from "typescript";
import * as util from "./util";
import { log } from "./util";
import * as os from "./os";
import * as sourceMaps from "./v8_source_maps";
import { _global, globalEval } from "./globals";
import * as deno from "./deno";
const EOL = "\n";
// tslint:disable-next-line:no-any
export type AmdFactory = (...args: any[]) => undefined | object;
export type AmdDefine = (deps: string[], factory: AmdFactory) => void;
// Uncaught exceptions are sent to window.onerror by v8worker2.
// https://git.io/vhOsf
window.onerror = (message, source, lineno, colno, error) => {
// TODO Currently there is a bug in v8_source_maps.ts that causes a segfault
// if it is used within window.onerror. To workaround we uninstall the
// Error.prepareStackTrace handler. Users will get unmapped stack traces on
// uncaught exceptions until this issue is fixed.
Error.prepareStackTrace = null;
console.log(error.message, error.stack);
os.exit(1);
};
export function setup(mainJs: string, mainMap: string): void {
sourceMaps.install({
installPrepareStackTrace: true,
getGeneratedContents: (filename: string): string => {
if (filename === "/main.js") {
return mainJs;
} else if (filename === "/main.map") {
return mainMap;
} else {
const mod = FileModule.load(filename);
if (!mod) {
console.error("getGeneratedContents cannot find", filename);
}
return mod.outputCode;
}
}
});
}
// This class represents a module. We call it FileModule to make it explicit
// that each module represents a single file.
// Access to FileModule instances should only be done thru the static method
// FileModule.load(). FileModules are NOT executed upon first load, only when
// compileAndRun is called.
export class FileModule {
scriptVersion: string;
readonly exports = {};
private static readonly map = new Map<string, FileModule>();
constructor(
readonly fileName: string,
readonly sourceCode = "",
public outputCode = ""
) {
util.assert(
!FileModule.map.has(fileName),
`FileModule.map already has ${fileName}`
);
FileModule.map.set(fileName, this);
if (outputCode !== "") {
this.scriptVersion = "1";
}
}
compileAndRun(): void {
if (!this.outputCode) {
// If there is no cached outputCode, then compile the code.
util.assert(
this.sourceCode != null && this.sourceCode.length > 0,
`Have no source code from ${this.fileName}`
);
const compiler = Compiler.instance();
this.outputCode = compiler.compile(this.fileName);
os.codeCache(this.fileName, this.sourceCode, this.outputCode);
}
util.log("compileAndRun", this.sourceCode);
execute(this.fileName, this.outputCode);
}
static load(fileName: string): FileModule {
return this.map.get(fileName);
}
static getScriptsWithSourceCode(): string[] {
const out = [];
for (const fn of this.map.keys()) {
const m = this.map.get(fn);
if (m.sourceCode) {
out.push(fn);
}
}
return out;
}
}
export function makeDefine(fileName: string): AmdDefine {
const localDefine = (deps: string[], factory: AmdFactory): void => {
const localRequire = (x: string) => {
log("localRequire", x);
};
const currentModule = FileModule.load(fileName);
const localExports = currentModule.exports;
log("localDefine", fileName, deps, localExports);
const args = deps.map(dep => {
if (dep === "require") {
return localRequire;
} else if (dep === "exports") {
return localExports;
} else if (dep === "typescript") {
return ts;
} else if (dep === "deno") {
return deno;
} else {
const resolved = resolveModuleName(dep, fileName);
const depModule = FileModule.load(resolved);
depModule.compileAndRun();
return depModule.exports;
}
});
factory(...args);
};
return localDefine;
}
export function resolveModule(
moduleSpecifier: string,
containingFile: string
): null | FileModule {
//util.log("resolveModule", { moduleSpecifier, containingFile });
util.assert(moduleSpecifier != null && moduleSpecifier.length > 0);
// We ask golang to sourceCodeFetch. It will load the sourceCode and if
// there is any outputCode cached, it will return that as well.
let fetchResponse;
try {
fetchResponse = os.codeFetch(moduleSpecifier, containingFile);
} catch (e) {
// TODO Only catch "no such file or directory" errors. Need error codes.
return null;
}
const { filename, sourceCode, outputCode } = fetchResponse;
if (sourceCode.length === 0) {
return null;
}
util.log("resolveModule sourceCode length ", sourceCode.length);
const m = FileModule.load(filename);
if (m != null) {
return m;
} else {
return new FileModule(filename, sourceCode, outputCode);
}
}
function resolveModuleName(
moduleSpecifier: string,
containingFile: string
): string | undefined {
const mod = resolveModule(moduleSpecifier, containingFile);
if (mod) {
return mod.fileName;
} else {
return undefined;
}
}
function execute(fileName: string, outputCode: string): void {
util.assert(outputCode && outputCode.length > 0);
_global["define"] = makeDefine(fileName);
outputCode += `\n//# sourceURL=${fileName}`;
globalEval(outputCode);
_global["define"] = null;
}
// This is a singleton class. Use Compiler.instance() to access.
class Compiler {
options: ts.CompilerOptions = {
allowJs: true,
module: ts.ModuleKind.AMD,
outDir: "$deno$",
inlineSourceMap: true,
lib: ["es2017"],
inlineSources: true,
target: ts.ScriptTarget.ES2017
};
/*
allowJs: true,
module: ts.ModuleKind.AMD,
noEmit: false,
outDir: '$deno$',
*/
private service: ts.LanguageService;
private constructor() {
const host = new TypeScriptHost(this.options);
this.service = ts.createLanguageService(host);
}
private static _instance: Compiler;
static instance(): Compiler {
return this._instance || (this._instance = new this());
}
compile(fileName: string): string {
const output = this.service.getEmitOutput(fileName);
// Get the relevant diagnostics - this is 3x faster than
// `getPreEmitDiagnostics`.
const diagnostics = this.service
.getCompilerOptionsDiagnostics()
.concat(this.service.getSyntacticDiagnostics(fileName))
.concat(this.service.getSemanticDiagnostics(fileName));
if (diagnostics.length > 0) {
const errMsg = ts.formatDiagnosticsWithColorAndContext(
diagnostics,
formatDiagnosticsHost
);
console.log(errMsg);
os.exit(1);
}
util.assert(!output.emitSkipped);
const outputCode = output.outputFiles[0].text;
// let sourceMapCode = output.outputFiles[0].text;
return outputCode;
}
}
// Create the compiler host for type checking.
class TypeScriptHost implements ts.LanguageServiceHost {
constructor(readonly options: ts.CompilerOptions) {}
getScriptFileNames(): string[] {
const keys = FileModule.getScriptsWithSourceCode();
util.log("getScriptFileNames", keys);
return keys;
}
getScriptVersion(fileName: string): string {
util.log("getScriptVersion", fileName);
const m = FileModule.load(fileName);
return m.scriptVersion;
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
util.log("getScriptSnapshot", fileName);
const m = resolveModule(fileName, ".");
if (m == null) {
util.log("getScriptSnapshot", fileName, "NOT FOUND");
return undefined;
}
//const m = resolveModule(fileName, ".");
util.assert(m.sourceCode.length > 0);
return ts.ScriptSnapshot.fromString(m.sourceCode);
}
fileExists(fileName: string): boolean {
const m = resolveModule(fileName, ".");
const exists = m != null;
util.log("fileExist", fileName, exists);
return exists;
}
readFile(path: string, encoding?: string): string | undefined {
util.log("readFile", path);
throw Error("not implemented");
}
getNewLine() {
return EOL;
}
getCurrentDirectory() {
util.log("getCurrentDirectory");
return ".";
}
getCompilationSettings() {
util.log("getCompilationSettings");
return this.options;
}
getDefaultLibFileName(options: ts.CompilerOptions): string {
const fn = ts.getDefaultLibFileName(options);
util.log("getDefaultLibFileName", fn);
const m = resolveModule(fn, "/$asset$/");
return m.fileName;
}
resolveModuleNames(
moduleNames: string[],
containingFile: string,
reusedNames?: string[]
): Array<ts.ResolvedModule | undefined> {
//util.log("resolveModuleNames", { moduleNames, reusedNames });
return moduleNames.map((name: string) => {
let resolvedFileName;
if (name === "deno") {
resolvedFileName = resolveModuleName("deno.d.ts", "/$asset$/");
} else if (name === "typescript") {
resolvedFileName = resolveModuleName("typescript.d.ts", "/$asset$/");
} else {
resolvedFileName = resolveModuleName(name, containingFile);
if (resolvedFileName == null) {
return undefined;
}
}
const isExternalLibraryImport = false;
return { resolvedFileName, isExternalLibraryImport };
});
}
}
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
getCurrentDirectory(): string {
return ".";
},
getCanonicalFileName(fileName: string): string {
return fileName;
},
getNewLine(): string {
return EOL;
}
};

View file

@ -1 +0,0 @@
console.log("Hello World");

View file

@ -1 +0,0 @@
Hello World

View file

@ -1 +0,0 @@
console.log("Hello World");

View file

@ -1 +0,0 @@
Hello World

View file

@ -1,3 +0,0 @@
import { printHello } from "./subdir/print_hello.ts";
printHello();

View file

@ -1 +0,0 @@
Hello

View file

@ -1,11 +0,0 @@
setTimeout(() => {
console.log("World");
}, 10);
console.log("Hello");
const id = setTimeout(() => {
console.log("Not printed");
}, 10000);
clearTimeout(id);

View file

@ -1,2 +0,0 @@
Hello
World

View file

@ -1,11 +0,0 @@
import { returnsHi, returnsFoo2, printHello3 } from "./subdir/mod1.ts";
printHello3();
if (returnsHi() !== "Hi") {
throw Error("Unexpected");
}
if (returnsFoo2() !== "Foo") {
throw Error("Unexpected");
}

View file

@ -1 +0,0 @@
Hello

View file

@ -1,3 +0,0 @@
import { printHello } from "http://localhost:4545/testdata/subdir/print_hello.ts";
printHello();
console.log("success");

View file

@ -1,3 +0,0 @@
Downloading http://localhost:4545/testdata/subdir/print_hello.ts
Hello
success

View file

@ -1,7 +0,0 @@
import { throwsError } from "./subdir/mod1.ts";
function foo() {
throwsError();
}
foo();

View file

@ -1,18 +0,0 @@
import * as deno from "deno";
deno.sub("echo", (ui8: Uint8Array) => {
const str = String.fromCharCode.apply(null, ui8);
console.log("Got message", str);
});
function str2ui8(str: string): Uint8Array {
const ui8 = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
ui8[i] = str.charCodeAt(i);
}
return ui8;
}
console.log("Before deno.pub()");
deno.pub("echo", str2ui8("hello"));
console.log("After deno.pub()");

View file

@ -1,3 +0,0 @@
Before deno.pub()
After deno.pub()
Got message hello

View file

@ -1,7 +0,0 @@
const id = setInterval(function() {
console.log("test")
}, 200);
setTimeout(function() {
clearInterval(id)
}, 500)

View file

@ -1,2 +0,0 @@
test
test

11
testdata/012_async.ts vendored
View file

@ -1,11 +0,0 @@
// Check that we can use the async keyword.
async function main() {
await new Promise((resolve, reject) => {
console.log("2");
setTimeout(resolve, 100);
});
console.log("3");
}
console.log("1");
main();

View file

@ -1,3 +0,0 @@
1
2
3

View file

@ -1,9 +0,0 @@
console.log("hello");
const foo = async () => {
console.log("before error");
throw Error("error");
}
foo();
console.log("world");

View file

@ -1,10 +0,0 @@
hello
before error
error Error: error
at foo ([WILDCARD]testdata/async_error.ts:4:11)
at eval ([WILDCARD]testdata/async_error.ts:6:1)
at Object.eval [as globalEval] (<anonymous>)
at execute (/main.js:[WILDCARD])
at FileModule.compileAndRun (/main.js:[WILDCARD])
at /main.js:[WILDCARD]
at /main.js:[WILDCARD]

9
testdata/error.ts vendored
View file

@ -1,9 +0,0 @@
function foo() {
throw Error("bad");
}
function bar() {
foo()
}
bar()

10
testdata/error.ts.out vendored
View file

@ -1,10 +0,0 @@
/main.js:[WILDCARD]
throw _iteratorError;
^
Error: bad
at foo ([WILDCARD]testdata/error.ts:2:9)
at bar ([WILDCARD]testdata/error.ts:6:3)
at eval ([WILDCARD]testdata/error.ts:9:1)
at Object.eval [as globalEval] (<anonymous>)
at execute (../runtime.ts:[WILDCARD])
at FileModule.compileAndRun (../runtime.ts:[WILDCARD]

View file

@ -1,3 +0,0 @@
import * as ts from "typescript";
console.log("typescript version", ts.version);

View file

@ -1 +0,0 @@
typescript version 2.8.3

View file

@ -1,17 +0,0 @@
import { returnsFoo, printHello2 } from "./subdir2/mod2.ts";
export function returnsHi(): string {
return "Hi";
}
export function returnsFoo2(): string {
return returnsFoo();
}
export function printHello3(): void {
printHello2();
}
export function throwsError(): void {
throw Error("exception from mod1");
}

View file

@ -1,3 +0,0 @@
export function printHello(): void {
console.log("Hello");
}

View file

@ -1,9 +0,0 @@
import { printHello } from "../print_hello.ts";
export function returnsFoo(): string {
return "Foo";
}
export function printHello2(): void {
printHello();
}

View file

@ -1,96 +0,0 @@
/*!
Copyright 2018 Propel http://propel.site/. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export { assert, assertEqual, equal } from "./util.ts";
export type TestFunction = () => void | Promise<void>;
export interface TestDefinition {
fn: TestFunction;
name: string;
}
export const exitOnFail = true;
/* A subset of the tests can be ran by providing a filter expression.
* In Node.js the filter is specified on the command line:
*
* ts-node test_node log # all tests with 'log' in the name
* ts-node test_node ^util # tests starting with 'util'
*
* In the browser, the filter is specified as part of the url:
*
* http://localhost:9876/test.html#script=some/script.js&filter=log
* http://localhost:9876/test.html#script=some/script.js&filter=^util
*/
let filterExpr: string = null;
const filterRegExp = filterExpr ? new RegExp(filterExpr, "i") : null;
const tests: TestDefinition[] = [];
export function test(t: TestDefinition | TestFunction): void {
const fn: TestFunction = typeof t === "function" ? t : t.fn;
const name: string = t.name;
if (!name) {
throw new Error("Test function may not be anonymous");
}
if (filter(name)) {
tests.push({ fn, name });
}
}
function filter(name: string): boolean {
if (filterRegExp) {
return filterRegExp.test(name);
} else {
return true;
}
}
async function runTests() {
let passed = 0;
let failed = 0;
for (let i = 0; i < tests.length; i++) {
const { fn, name } = tests[i];
console.log(`${i+1}/${tests.length} +${passed} -${failed}: ${name}`);
try {
await fn();
passed++;
} catch (e) {
console.error("\nTest FAIL", name);
console.error((e && e.stack) || e);
failed++;
if (exitOnFail) {
break;
}
}
}
console.log(`\nDONE. Test passed: ${passed}, failed: ${failed}`);
if (failed === 0) {
// All good.
} else {
// Use setTimeout to avoid the error being ignored due to unhandled
// promise rejections being swallowed.
setTimeout(() => {
throw new Error(`There were ${failed} test failures.`);
}, 0);
}
}
setTimeout(runTests, 0);

View file

@ -1,60 +0,0 @@
/*!
Copyright 2018 Propel http://propel.site/. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// tslint:disable-next-line:no-any
export function assertEqual(actual: any, expected: any, msg?: string) {
if (!msg) { msg = `actual: ${actual} expected: ${expected}`; }
if (!equal(actual, expected)) {
console.error(
"assertEqual failed. actual = ", actual, "expected =", expected);
throw new Error(msg);
}
}
export function assert(expr: boolean, msg = "") {
if (!expr) {
throw new Error(msg);
}
}
// tslint:disable-next-line:no-any
export function equal(c: any, d: any): boolean {
const seen = new Map();
return (function compare(a, b) {
if (a === b) {
return true;
}
if (typeof a === "number" && typeof b === "number" &&
isNaN(a) && isNaN(b)) {
return true;
}
if (a && typeof a === "object" && b && typeof b === "object") {
if (seen.get(a) === b) {
return true;
}
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
for (const key in { ...a, ...b }) {
if (!compare(a[key], b[key])) {
return false;
}
}
seen.set(a, b);
return true;
}
return false;
})(c, d);
}

View file

@ -1,32 +0,0 @@
/*!
Copyright 2018 Propel http://propel.site/. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { test } from "./testing.ts";
import { assert } from "./util.ts";
import * as util from "./util.ts";
test(async function util_equal() {
assert(util.equal("world", "world"));
assert(!util.equal("hello", "world"));
assert(util.equal(5, 5));
assert(!util.equal(5, 6));
assert(util.equal(NaN, NaN));
assert(util.equal({ hello: "world" }, { hello: "world" }));
assert(!util.equal({ world: "hello" }, { hello: "world" }));
assert(util.equal({ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone" } }));
assert(!util.equal({ hello: "world", hi: { there: "everyone" } },
{ hello: "world", hi: { there: "everyone else" } }));
});

126
tests.ts
View file

@ -1,126 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// This test is executed as part of integration_test.go
// But it can also be run manually:
// ./deno tests.ts
// There must also be a static file http server running on localhost:4545
// serving the deno project directory. Try this:
// http-server -p 4545 --cors .
import { test, assert, assertEqual } from "./testing/testing.ts";
import { readFileSync, writeFileSync } from "deno";
test(async function tests_test() {
assert(true);
});
test(async function tests_fetch() {
const response = await fetch("http://localhost:4545/package.json");
const json = await response.json();
assertEqual(json.name, "deno");
});
test(function tests_console_assert() {
console.assert(true);
let hasThrown = false;
try {
console.assert(false);
} catch {
hasThrown = true;
}
assertEqual(hasThrown, true);
});
test(async function tests_readFileSync() {
const data = readFileSync("package.json");
if (!data.byteLength) {
throw Error(
`Expected positive value for data.byteLength ${data.byteLength}`
);
}
const decoder = new TextDecoder("utf-8");
const json = decoder.decode(data);
const pkg = JSON.parse(json);
assertEqual(pkg.name, "deno");
});
test(async function tests_writeFileSync() {
const enc = new TextEncoder();
const data = enc.encode("Hello");
// TODO need ability to get tmp dir.
const fn = "/tmp/test.txt";
writeFileSync("/tmp/test.txt", data, 0o666);
const dataRead = readFileSync("/tmp/test.txt");
const dec = new TextDecoder("utf-8");
const actual = dec.decode(dataRead);
assertEqual("Hello", actual);
});
test(function tests_console_assert() {
console.assert(true);
let hasThrown = false;
try {
console.assert(false);
} catch {
hasThrown = true;
}
assertEqual(hasThrown, true);
});
test(function tests_console_stringify_circular() {
class Base {
a = 1;
m1() {}
}
class Extended extends Base {
b = 2;
m2() {}
}
// tslint:disable-next-line:no-any
const nestedObj: any = {
num: 1,
bool: true,
str: "a",
method() {},
un: undefined,
nu: null,
arrowFunc: () => {},
extendedClass: new Extended(),
nFunc: new Function(),
extendedCstr: Extended
};
const circularObj = {
num: 2,
bool: false,
str: "b",
method() {},
un: undefined,
nu: null,
nested: nestedObj,
emptyObj: {},
arr: [1, "s", false, null, nestedObj],
baseClass: new Base()
};
nestedObj.o = circularObj;
try {
console.log(1);
console.log("s");
console.log(false);
console.log(Symbol(1));
console.log(null);
console.log(undefined);
console.log(new Extended());
console.log(function f() {});
console.log(nestedObj);
console.log(JSON);
console.log(console);
} catch {
throw new Error("Expected no crash on circular object");
}
});

6
text-encoding.d.ts vendored
View file

@ -1,6 +0,0 @@
// Remove and depend on @types/text-encoding once this PR is merged
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26141
declare module "text-encoding" {
export const TextEncoder: TextEncoder;
export const TextDecoder: TextDecoder;
}

View file

@ -1,79 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"github.com/golang/protobuf/proto"
"time"
)
type Timer struct {
Id int32
Done bool
Cleared bool
Interval bool
Delay int32 // In milliseconds
}
var timers = make(map[int32]*Timer)
func InitTimers() {
Sub("timers", func(buf []byte) []byte {
msg := &Msg{}
check(proto.Unmarshal(buf, msg))
switch msg.Command {
case Msg_TIMER_START:
id := msg.TimerStartId
t := &Timer{
Id: id,
Done: false,
Interval: msg.TimerStartInterval,
Delay: msg.TimerStartDelay,
Cleared: false,
}
if t.Delay < 10 {
t.Delay = 10
}
t.StartTimer()
timers[id] = t
return nil
case Msg_TIMER_CLEAR:
// TODO maybe need mutex here.
timer := timers[msg.TimerClearId]
timer.Clear()
default:
panic("[timers] Unexpected message " + string(buf))
}
return nil
})
}
func (t *Timer) Clear() {
if !t.Cleared {
wg.Done()
t.Cleared = true
delete(timers, t.Id)
}
t.Done = true
}
func (t *Timer) StartTimer() {
wg.Add(1)
go func() {
defer t.Clear()
for {
time.Sleep(time.Duration(t.Delay) * time.Millisecond)
if !t.Interval {
t.Done = true
}
PubMsg("timers", &Msg{
Command: Msg_TIMER_READY,
TimerReadyId: t.Id,
TimerReadyDone: t.Done,
})
if t.Done {
return
}
}
}()
}

View file

@ -1,89 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import { deno as pb } from "./msg.pb";
import { pubInternal, sub } from "./dispatch";
import { assert } from "./util";
let nextTimerId = 1;
// tslint:disable-next-line:no-any
export type TimerCallback = (...args: any[]) => void;
interface Timer {
id: number;
cb: TimerCallback;
interval: boolean;
// tslint:disable-next-line:no-any
args: any[];
delay: number; // milliseconds
}
const timers = new Map<number, Timer>();
export function initTimers() {
sub("timers", onMessage);
}
function onMessage(payload: Uint8Array) {
const msg = pb.Msg.decode(payload);
assert(msg.command === pb.Msg.Command.TIMER_READY);
const { timerReadyId, timerReadyDone } = msg;
const timer = timers.get(timerReadyId);
if (!timer) {
return;
}
timer.cb(...timer.args);
if (timerReadyDone) {
timers.delete(timerReadyId);
}
}
function setTimer(
cb: TimerCallback,
delay: number,
interval: boolean,
// tslint:disable-next-line:no-any
args: any[]
): number {
const timer = {
id: nextTimerId++,
interval,
delay,
args,
cb
};
timers.set(timer.id, timer);
pubInternal("timers", {
command: pb.Msg.Command.TIMER_START,
timerStartId: timer.id,
timerStartInterval: timer.interval,
timerStartDelay: timer.delay
});
return timer.id;
}
export function setTimeout(
cb: TimerCallback,
delay: number,
// tslint:disable-next-line:no-any
...args: any[]
): number {
return setTimer(cb, delay, false, args);
}
export function setInterval(
cb: TimerCallback,
delay: number,
// tslint:disable-next-line:no-any
...args: any[]
): number {
return setTimer(cb, delay, true, args);
}
export function clearTimer(id: number) {
timers.delete(id);
pubInternal("timers", {
command: pb.Msg.Command.TIMER_CLEAR,
timerClearId: id
});
}

View file

@ -1,25 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"noImplicitAny": true,
"baseUrl": ".",
"removeComments": true,
"preserveConstEnums": true,
"declaration": true,
"target": "es2017",
"lib": ["es2017", "dom"],
"noEmit": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedParameters": false,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"allowUnreachableCode": false,
"experimentalDecorators": true
},
"include": ["*.ts", "*.js"],
"exclude": ["tests.ts"]
}

View file

@ -1,67 +0,0 @@
{
"rules": {
"array-type": [true, "array-simple"],
"arrow-return-shorthand": true,
"ban": [
true,
["fit"],
["fdescribe"],
["xit"],
["xdescribe"],
["fitAsync"],
["xitAsync"],
["fitFakeAsync"],
["xitFakeAsync"]
],
"ban-types": [
true,
["Object", "Use {} instead."],
["String", "Use 'string' instead."],
["Number", "Use 'number' instead."],
["Boolean", "Use 'boolean' instead."]
],
"class-name": true,
"curly": true,
"interface-name": [true, "never-prefix"],
"jsdoc-format": true,
"forin": false,
"label-position": true,
"max-line-length": [true, 80],
"new-parens": true,
"no-angle-bracket-type-assertion": true,
"no-any": true,
"no-construct": true,
"no-consecutive-blank-lines": true,
"no-debugger": true,
"no-default-export": true,
"no-inferrable-types": true,
//"no-namespace": [true, "allow-declarations"],
"no-reference": true,
"no-require-imports": true,
"no-string-throw": true,
"no-unused-expression": true,
"no-unused-variable": true,
"no-var-keyword": true,
"object-literal-shorthand": true,
"only-arrow-functions": [
true,
"allow-declarations",
"allow-named-functions"
],
"prefer-const": true,
"quotemark": [true, "double"],
"radix": true,
"restrict-plus-operands": true,
"semicolon": [true, "always", "ignore-bound-class-methods"],
"switch-default": true,
"triple-equals": [true, "allow-null-check"],
"use-isnan": true,
"variable-name": [
true,
"check-format",
"ban-keywords",
"allow-leading-underscore",
"allow-trailing-underscore"
]
}
}

View file

@ -1,10 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
export type TypedArray = Uint8Array | Float32Array | Int32Array;
export interface ModuleInfo {
moduleName?: string;
filename?: string;
sourceCode?: string;
outputCode?: string;
}

671
url.js
View file

@ -1,671 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
* Forked from https://github.com/github/url-polyfill
* Version 16c1aa Feb 9 2018.
*/
(function(scope) {
"use strict";
// feature detect for URL constructor
var hasWorkingUrl = false;
if (!scope.forceJURL) {
try {
var u = new URL("b", "http://a");
u.pathname = "c%20d";
hasWorkingUrl = u.href === "http://a/c%20d";
} catch (e) {}
}
if (hasWorkingUrl) return;
var relative = Object.create(null);
relative["ftp"] = 21;
relative["file"] = 0;
relative["gopher"] = 70;
relative["http"] = 80;
relative["https"] = 443;
relative["ws"] = 80;
relative["wss"] = 443;
var relativePathDotMapping = Object.create(null);
relativePathDotMapping["%2e"] = ".";
relativePathDotMapping[".%2e"] = "..";
relativePathDotMapping["%2e."] = "..";
relativePathDotMapping["%2e%2e"] = "..";
function isRelativeScheme(scheme) {
return relative[scheme] !== undefined;
}
function invalid() {
clear.call(this);
this._isInvalid = true;
}
function IDNAToASCII(h) {
if ("" == h) {
invalid.call(this);
}
// XXX
return h.toLowerCase();
}
function percentEscape(c) {
var unicode = c.charCodeAt(0);
if (
unicode > 0x20 &&
unicode < 0x7f &&
// " # < > ? `
[0x22, 0x23, 0x3c, 0x3e, 0x3f, 0x60].indexOf(unicode) == -1
) {
return c;
}
return encodeURIComponent(c);
}
function percentEscapeQuery(c) {
// XXX This actually needs to encode c using encoding and then
// convert the bytes one-by-one.
var unicode = c.charCodeAt(0);
if (
unicode > 0x20 &&
unicode < 0x7f &&
// " # < > ` (do not escape '?')
[0x22, 0x23, 0x3c, 0x3e, 0x60].indexOf(unicode) == -1
) {
return c;
}
return encodeURIComponent(c);
}
var EOF = undefined,
ALPHA = /[a-zA-Z]/,
ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
function parse(input, stateOverride, base) {
function err(message) {
errors.push(message);
}
var state = stateOverride || "scheme start",
cursor = 0,
buffer = "",
seenAt = false,
seenBracket = false,
errors = [];
loop: while (
(input[cursor - 1] != EOF || cursor == 0) &&
!this._isInvalid
) {
var c = input[cursor];
switch (state) {
case "scheme start":
if (c && ALPHA.test(c)) {
buffer += c.toLowerCase(); // ASCII-safe
state = "scheme";
} else if (!stateOverride) {
buffer = "";
state = "no scheme";
continue;
} else {
err("Invalid scheme.");
break loop;
}
break;
case "scheme":
if (c && ALPHANUMERIC.test(c)) {
buffer += c.toLowerCase(); // ASCII-safe
} else if (":" == c) {
this._scheme = buffer;
buffer = "";
if (stateOverride) {
break loop;
}
if (isRelativeScheme(this._scheme)) {
this._isRelative = true;
}
if ("file" == this._scheme) {
state = "relative";
} else if (
this._isRelative &&
base &&
base._scheme == this._scheme
) {
state = "relative or authority";
} else if (this._isRelative) {
state = "authority first slash";
} else {
state = "scheme data";
}
} else if (!stateOverride) {
buffer = "";
cursor = 0;
state = "no scheme";
continue;
} else if (EOF == c) {
break loop;
} else {
err("Code point not allowed in scheme: " + c);
break loop;
}
break;
case "scheme data":
if ("?" == c) {
query = "?";
state = "query";
} else if ("#" == c) {
this._fragment = "#";
state = "fragment";
} else {
// XXX error handling
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._schemeData += percentEscape(c);
}
}
break;
case "no scheme":
if (!base || !isRelativeScheme(base._scheme)) {
err("Missing scheme.");
invalid.call(this);
} else {
state = "relative";
continue;
}
break;
case "relative or authority":
if ("/" == c && "/" == input[cursor + 1]) {
state = "authority ignore slashes";
} else {
err("Expected /, got: " + c);
state = "relative";
continue;
}
break;
case "relative":
this._isRelative = true;
if ("file" != this._scheme) this._scheme = base._scheme;
if (EOF == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
this._username = base._username;
this._password = base._password;
break loop;
} else if ("/" == c || "\\" == c) {
if ("\\" == c) err("\\ is an invalid code point.");
state = "relative slash";
} else if ("?" == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = "?";
this._username = base._username;
this._password = base._password;
state = "query";
} else if ("#" == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
this._fragment = "#";
this._username = base._username;
this._password = base._password;
state = "fragment";
} else {
var nextC = input[cursor + 1];
var nextNextC = input[cursor + 2];
if (
"file" != this._scheme ||
!ALPHA.test(c) ||
(nextC != ":" && nextC != "|") ||
(EOF != nextNextC &&
"/" != nextNextC &&
"\\" != nextNextC &&
"?" != nextNextC &&
"#" != nextNextC)
) {
this._host = base._host;
this._port = base._port;
this._username = base._username;
this._password = base._password;
this._path = base._path.slice();
this._path.pop();
}
state = "relative path";
continue;
}
break;
case "relative slash":
if ("/" == c || "\\" == c) {
if ("\\" == c) {
err("\\ is an invalid code point.");
}
if ("file" == this._scheme) {
state = "file host";
} else {
state = "authority ignore slashes";
}
} else {
if ("file" != this._scheme) {
this._host = base._host;
this._port = base._port;
this._username = base._username;
this._password = base._password;
}
state = "relative path";
continue;
}
break;
case "authority first slash":
if ("/" == c) {
state = "authority second slash";
} else {
err("Expected '/', got: " + c);
state = "authority ignore slashes";
continue;
}
break;
case "authority second slash":
state = "authority ignore slashes";
if ("/" != c) {
err("Expected '/', got: " + c);
continue;
}
break;
case "authority ignore slashes":
if ("/" != c && "\\" != c) {
state = "authority";
continue;
} else {
err("Expected authority, got: " + c);
}
break;
case "authority":
if ("@" == c) {
if (seenAt) {
err("@ already seen.");
buffer += "%40";
}
seenAt = true;
for (var i = 0; i < buffer.length; i++) {
var cp = buffer[i];
if ("\t" == cp || "\n" == cp || "\r" == cp) {
err("Invalid whitespace in authority.");
continue;
}
// XXX check URL code points
if (":" == cp && null === this._password) {
this._password = "";
continue;
}
var tempC = percentEscape(cp);
null !== this._password
? (this._password += tempC)
: (this._username += tempC);
}
buffer = "";
} else if (
EOF == c ||
"/" == c ||
"\\" == c ||
"?" == c ||
"#" == c
) {
cursor -= buffer.length;
buffer = "";
state = "host";
continue;
} else {
buffer += c;
}
break;
case "file host":
if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
if (
buffer.length == 2 &&
ALPHA.test(buffer[0]) &&
(buffer[1] == ":" || buffer[1] == "|")
) {
state = "relative path";
} else if (buffer.length == 0) {
state = "relative path start";
} else {
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "relative path start";
}
continue;
} else if ("\t" == c || "\n" == c || "\r" == c) {
err("Invalid whitespace in file host.");
} else {
buffer += c;
}
break;
case "host":
case "hostname":
if (":" == c && !seenBracket) {
// XXX host parsing
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "port";
if ("hostname" == stateOverride) {
break loop;
}
} else if (
EOF == c ||
"/" == c ||
"\\" == c ||
"?" == c ||
"#" == c
) {
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "relative path start";
if (stateOverride) {
break loop;
}
continue;
} else if ("\t" != c && "\n" != c && "\r" != c) {
if ("[" == c) {
seenBracket = true;
} else if ("]" == c) {
seenBracket = false;
}
buffer += c;
} else {
err("Invalid code point in host/hostname: " + c);
}
break;
case "port":
if (/[0-9]/.test(c)) {
buffer += c;
} else if (
EOF == c ||
"/" == c ||
"\\" == c ||
"?" == c ||
"#" == c ||
stateOverride
) {
if ("" != buffer) {
var temp = parseInt(buffer, 10);
if (temp != relative[this._scheme]) {
this._port = temp + "";
}
buffer = "";
}
if (stateOverride) {
break loop;
}
state = "relative path start";
continue;
} else if ("\t" == c || "\n" == c || "\r" == c) {
err("Invalid code point in port: " + c);
} else {
invalid.call(this);
}
break;
case "relative path start":
if ("\\" == c) err("'\\' not allowed in path.");
state = "relative path";
if ("/" != c && "\\" != c) {
continue;
}
break;
case "relative path":
if (
EOF == c ||
"/" == c ||
"\\" == c ||
(!stateOverride && ("?" == c || "#" == c))
) {
if ("\\" == c) {
err("\\ not allowed in relative path.");
}
var tmp;
if ((tmp = relativePathDotMapping[buffer.toLowerCase()])) {
buffer = tmp;
}
if (".." == buffer) {
this._path.pop();
if ("/" != c && "\\" != c) {
this._path.push("");
}
} else if ("." == buffer && "/" != c && "\\" != c) {
this._path.push("");
} else if ("." != buffer) {
if (
"file" == this._scheme &&
this._path.length == 0 &&
buffer.length == 2 &&
ALPHA.test(buffer[0]) &&
buffer[1] == "|"
) {
buffer = buffer[0] + ":";
}
this._path.push(buffer);
}
buffer = "";
if ("?" == c) {
this._query = "?";
state = "query";
} else if ("#" == c) {
this._fragment = "#";
state = "fragment";
}
} else if ("\t" != c && "\n" != c && "\r" != c) {
buffer += percentEscape(c);
}
break;
case "query":
if (!stateOverride && "#" == c) {
this._fragment = "#";
state = "fragment";
} else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._query += percentEscapeQuery(c);
}
break;
case "fragment":
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._fragment += c;
}
break;
}
cursor++;
}
}
function clear() {
this._scheme = "";
this._schemeData = "";
this._username = "";
this._password = null;
this._host = "";
this._port = "";
this._path = [];
this._query = "";
this._fragment = "";
this._isInvalid = false;
this._isRelative = false;
}
// Does not process domain names or IP addresses.
// Does not handle encoding for the query parameter.
function jURL(url, base /* , encoding */) {
if (base !== undefined && !(base instanceof jURL))
base = new jURL(String(base));
url = String(url);
this._url = url;
clear.call(this);
var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
// encoding = encoding || 'utf-8'
parse.call(this, input, null, base);
}
jURL.prototype = {
toString: function() {
return this.href;
},
get href() {
if (this._isInvalid) return this._url;
var authority = "";
if ("" != this._username || null != this._password) {
authority =
this._username +
(null != this._password ? ":" + this._password : "") +
"@";
}
return (
this.protocol +
(this._isRelative ? "//" + authority + this.host : "") +
this.pathname +
this._query +
this._fragment
);
},
set href(href) {
clear.call(this);
parse.call(this, href);
},
get protocol() {
return this._scheme + ":";
},
set protocol(protocol) {
if (this._isInvalid) return;
parse.call(this, protocol + ":", "scheme start");
},
get host() {
return this._isInvalid
? ""
: this._port
? this._host + ":" + this._port
: this._host;
},
set host(host) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, host, "host");
},
get hostname() {
return this._host;
},
set hostname(hostname) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, hostname, "hostname");
},
get port() {
return this._port;
},
set port(port) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, port, "port");
},
get pathname() {
return this._isInvalid
? ""
: this._isRelative
? "/" + this._path.join("/")
: this._schemeData;
},
set pathname(pathname) {
if (this._isInvalid || !this._isRelative) return;
this._path = [];
parse.call(this, pathname, "relative path start");
},
get search() {
return this._isInvalid || !this._query || "?" == this._query
? ""
: this._query;
},
set search(search) {
if (this._isInvalid || !this._isRelative) return;
this._query = "?";
if ("?" == search[0]) search = search.slice(1);
parse.call(this, search, "query");
},
get hash() {
return this._isInvalid || !this._fragment || "#" == this._fragment
? ""
: this._fragment;
},
set hash(hash) {
if (this._isInvalid) return;
this._fragment = "#";
if ("#" == hash[0]) hash = hash.slice(1);
parse.call(this, hash, "fragment");
},
get origin() {
var host;
if (this._isInvalid || !this._scheme) {
return "";
}
// javascript: Gecko returns String(""), WebKit/Blink String("null")
// Gecko throws error for "data://"
// data: Gecko returns "", Blink returns "data://", WebKit returns "null"
// Gecko returns String("") for file: mailto:
// WebKit/Blink returns String("SCHEME://") for file: mailto:
switch (this._scheme) {
case "data":
case "file":
case "javascript":
case "mailto":
return "null";
}
host = this.host;
if (!host) {
return "";
}
return this._scheme + "://" + host;
}
};
// Copy over the static methods
var OriginalURL = scope.URL;
if (OriginalURL) {
jURL.createObjectURL = function(blob) {
// IE extension allows a second optional options argument.
// http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
};
jURL.revokeObjectURL = function(url) {
OriginalURL.revokeObjectURL(url);
};
}
scope.URL = jURL;
})(window);

106
util.go
View file

@ -1,106 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
package deno
import (
"fmt"
"net/url"
"os"
"strings"
)
func logDebug(format string, v ...interface{}) {
// Unless the debug flag is specified, discard logs.
if *flagDebug {
fmt.Printf(format+"\n", v...)
}
}
// exists returns whether the given file or directory exists or not
func exists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
panic(err)
}
func assert(cond bool, msg string) {
if !cond {
panic(msg)
}
}
func isRemote(filename string) bool {
u, err := url.Parse(filename)
check(err)
return u.IsAbs()
}
func check(e error) {
if e != nil {
panic(e)
}
}
func exitOnError(err error) {
if err != nil {
os.Stderr.WriteString(err.Error())
os.Exit(1)
}
}
func async(cb func()) {
wg.Add(1)
go func() {
cb()
wg.Done()
}()
}
const wildcard = "[WILDCARD]"
// Matches the pattern string against the text string. The pattern can
// contain "[WILDCARD]" substrings which will match one or more characters.
// Returns true if matched.
func patternMatch(pattern string, text string) bool {
// Empty pattern only match empty text.
if len(pattern) == 0 {
return len(text) == 0
}
if pattern == wildcard {
return true
}
parts := strings.Split(pattern, wildcard)
if len(parts) == 1 {
return pattern == text
}
if strings.HasPrefix(text, parts[0]) {
text = text[len(parts[0]):]
} else {
return false
}
for i := 1; i < len(parts); i++ {
// If the last part is empty, we match.
if i == len(parts)-1 {
if parts[i] == "" || parts[i] == "\n" {
return true
}
}
index := strings.Index(text, parts[i])
if index < 0 {
return false
}
text = text[index+len(parts[i]):]
}
return len(text) == 0
}

53
util.ts
View file

@ -1,53 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
import { debug } from "./main";
import { TypedArray } from "./types";
// Internal logging for deno. Use the "debug" variable above to control
// output.
// tslint:disable-next-line:no-any
export function log(...args: any[]): void {
if (debug) {
console.log(...args);
}
}
export function assert(cond: boolean, msg = "") {
if (!cond) {
throw Error(`Assert fail. ${msg}`);
}
}
export function typedArrayToArrayBuffer(ta: TypedArray): ArrayBuffer {
const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength);
return ab as ArrayBuffer;
}
export function arrayToStr(ui8: Uint8Array): string {
return String.fromCharCode(...ui8);
}
// A `Resolvable` is a Promise with the `reject` and `resolve` functions
// placed as methods on the promise object itself. It allows you to do:
//
// const p = createResolvable<number>();
// ...
// p.resolve(42);
//
// It'd be prettier to make Resolvable a class that inherits from Promise,
// rather than an interface. This is possible in ES2016, however typescript
// produces broken code when targeting ES5 code.
// See https://github.com/Microsoft/TypeScript/issues/15202
// At the time of writing, the github issue is closed but the problem remains.
export interface Resolvable<T> extends Promise<T> {
resolve: (value?: T | PromiseLike<T>) => void;
// tslint:disable-next-line:no-any
reject: (reason?: any) => void;
}
export function createResolvable<T>(): Resolvable<T> {
let methods;
const promise = new Promise<T>((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods) as Resolvable<T>;
}

View file

@ -1,62 +0,0 @@
package deno
import (
"testing"
)
const exStackTrace = `hello
before error
error Error: error
at foo (/Users/rld/go/src/github.com/ry/deno/testdata/013_async_throw.ts:4:11)
at eval (/Users/rld/go/src/github.com/ry/deno/testdata/013_async_throw.ts:6:1)
at Object.eval [as globalEval] (<anonymous>)
at execute (/main.js:144781:15)
at FileModule.compileAndRun (/main.js:144678:13)
at /main.js:145161:13
at /main.js:15733:13`
const exStackTracePattern = `hello
before error
error Error: error
at foo ([WILDCARD]testdata/013_async_throw.ts:4:11)
at eval ([WILDCARD]testdata/013_async_throw.ts:6:1)
at Object.eval [as globalEval] (<anonymous>)
at execute (/main.js:[WILDCARD]`
func TestPatternMatch(t *testing.T) {
if patternMatch("aa", "a") != false {
t.Fatalf("Wrong resullt (1).")
}
if patternMatch("aaa[WILDCARD]b", "aaaxsdfdb") != true {
t.Fatalf("Wrong resullt (2).")
}
if patternMatch("aab[WILDCARD]", "xsd") != false {
t.Fatalf("Wrong resullt (3).")
}
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abc") != true {
t.Fatalf("Wrong resullt (4).")
}
if patternMatch("a[WILDCARD]b[WILDCARD]c", "axbc") != true {
t.Fatalf("Wrong resullt (5).")
}
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abxc") != true {
t.Fatalf("Wrong resullt (6).")
}
if patternMatch("a[WILDCARD]b[WILDCARD]c", "axbxc") != true {
t.Fatalf("Wrong resullt (7).")
}
if patternMatch("a[WILDCARD]b[WILDCARD]c", "abcx") != false {
t.Fatalf("Wrong resullt (8).")
}
if patternMatch("a[WILDCARD][WILDCARD]c", "abc") != true {
t.Fatalf("Wrong resullt (9).")
}
if patternMatch("a[WILDCARD][WILDCARD]c", "ac") != true {
t.Fatalf("Wrong resullt (10).")
}
}
func TestPatternMatchStackTrace(t *testing.T) {
if patternMatch(exStackTracePattern, exStackTrace) != true {
t.Fatalf("Wrong resullt (11).")
}
}

View file

@ -1,273 +0,0 @@
// Copyright 2014 Evan Wallace
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// Originated from source-map-support but has been heavily modified for deno.
import { SourceMapConsumer, MappedPosition } from "source-map";
import * as base64 from "base64-js";
import { arrayToStr } from "./util";
const consumers = new Map<string, SourceMapConsumer>();
interface Options {
// A callback the returns generated file contents.
getGeneratedContents: GetGeneratedContentsCallback;
// Usually set the following to true. Set to false for testing.
installPrepareStackTrace: boolean;
}
interface CallSite extends NodeJS.CallSite {
getScriptNameOrSourceURL(): string;
}
interface Position {
source: string; // Filename
column: number;
line: number;
}
type GetGeneratedContentsCallback = (fileName: string) => string;
let getGeneratedContents: GetGeneratedContentsCallback;
export function install(options: Options) {
getGeneratedContents = options.getGeneratedContents;
if (options.installPrepareStackTrace) {
Error.prepareStackTrace = prepareStackTraceWrapper;
}
}
export function prepareStackTraceWrapper(
error: Error,
stack: CallSite[]
): string {
try {
return prepareStackTrace(error, stack);
} catch (prepareStackError) {
Error.prepareStackTrace = null;
console.log("=====Error inside of prepareStackTrace====");
console.log(prepareStackError.stack.toString());
console.log("=====Original error=======================");
throw error;
}
}
export function prepareStackTrace(error: Error, stack: CallSite[]): string {
const frames = stack.map(
(frame: CallSite) => `\n at ${wrapCallSite(frame).toString()}`
);
return error.toString() + frames.join("");
}
export function wrapCallSite(frame: CallSite): CallSite {
if (frame.isNative()) {
return frame;
}
// Most call sites will return the source file from getFileName(), but code
// passed to eval() ending in "//# sourceURL=..." will return the source file
// from getScriptNameOrSourceURL() instead
const source = frame.getFileName() || frame.getScriptNameOrSourceURL();
if (source) {
const line = frame.getLineNumber();
const column = frame.getColumnNumber() - 1;
const position = mapSourcePosition({ source, line, column });
frame = cloneCallSite(frame);
frame.getFileName = () => position.source;
frame.getLineNumber = () => position.line;
frame.getColumnNumber = () => Number(position.column) + 1;
frame.getScriptNameOrSourceURL = () => position.source;
frame.toString = () => CallSiteToString(frame);
return frame;
}
// Code called using eval() needs special handling
let origin = frame.isEval() && frame.getEvalOrigin();
if (origin) {
origin = mapEvalOrigin(origin);
frame = cloneCallSite(frame);
frame.getEvalOrigin = () => origin;
return frame;
}
// If we get here then we were unable to change the source position
return frame;
}
function cloneCallSite(frame: CallSite): CallSite {
// tslint:disable:no-any
const obj: any = {};
const frame_ = frame as any;
const props = Object.getOwnPropertyNames(Object.getPrototypeOf(frame));
props.forEach(name => {
obj[name] = /^(?:is|get)/.test(name)
? () => frame_[name].call(frame)
: frame_[name];
});
return (obj as any) as CallSite;
// tslint:enable:no-any
}
// Taken from source-map-support, original copied from V8's messages.js
// MIT License. Copyright (c) 2014 Evan Wallace
function CallSiteToString(frame: CallSite): string {
let fileName;
let fileLocation = "";
if (frame.isNative()) {
fileLocation = "native";
} else {
fileName = frame.getScriptNameOrSourceURL();
if (!fileName && frame.isEval()) {
fileLocation = frame.getEvalOrigin();
fileLocation += ", "; // Expecting source position to follow.
}
if (fileName) {
fileLocation += fileName;
} else {
// Source code does not originate from a file and is not native, but we
// can still get the source position inside the source string, e.g. in
// an eval string.
fileLocation += "<anonymous>";
}
const lineNumber = frame.getLineNumber();
if (lineNumber != null) {
fileLocation += ":" + String(lineNumber);
const columnNumber = frame.getColumnNumber();
if (columnNumber) {
fileLocation += ":" + String(columnNumber);
}
}
}
let line = "";
const functionName = frame.getFunctionName();
let addSuffix = true;
const isConstructor = frame.isConstructor();
const isMethodCall = !(frame.isToplevel() || isConstructor);
if (isMethodCall) {
let typeName = frame.getTypeName();
// Fixes shim to be backward compatable with Node v0 to v4
if (typeName === "[object Object]") {
typeName = "null";
}
const methodName = frame.getMethodName();
if (functionName) {
if (typeName && functionName.indexOf(typeName) !== 0) {
line += typeName + ".";
}
line += functionName;
if (
methodName &&
functionName.indexOf("." + methodName) !==
functionName.length - methodName.length - 1
) {
line += ` [as ${methodName} ]`;
}
} else {
line += typeName + "." + (methodName || "<anonymous>");
}
} else if (isConstructor) {
line += "new " + (functionName || "<anonymous>");
} else if (functionName) {
line += functionName;
} else {
line += fileLocation;
addSuffix = false;
}
if (addSuffix) {
line += ` (${fileLocation})`;
}
return line;
}
// Regex for detecting source maps
const reSourceMap = /^data:application\/json[^,]+base64,/;
function loadConsumer(source: string): SourceMapConsumer {
let consumer = consumers.get(source);
if (consumer == null) {
const code = getGeneratedContents(source);
if (!code) {
return null;
}
let sourceMappingURL = retrieveSourceMapURL(code);
if (!sourceMappingURL) {
throw Error("No source map?");
}
let sourceMapData: string;
if (reSourceMap.test(sourceMappingURL)) {
// Support source map URL as a data url
const rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(",") + 1);
const ui8 = base64.toByteArray(rawData);
sourceMapData = arrayToStr(ui8);
sourceMappingURL = source;
} else {
// Support source map URLs relative to the source URL
//sourceMappingURL = supportRelativeURL(source, sourceMappingURL);
sourceMapData = getGeneratedContents(sourceMappingURL);
}
//console.log("sourceMapData", sourceMapData);
const rawSourceMap = JSON.parse(sourceMapData);
consumer = new SourceMapConsumer(rawSourceMap);
consumers.set(source, consumer);
}
return consumer;
}
function retrieveSourceMapURL(fileData: string): string {
// Get the URL of the source map
// tslint:disable-next-line:max-line-length
const re = /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/)[ \t]*$)/gm;
// Keep executing the search to find the *last* sourceMappingURL to avoid
// picking up sourceMappingURLs from comments, strings, etc.
let lastMatch, match;
while ((match = re.exec(fileData))) {
lastMatch = match;
}
if (!lastMatch) {
return null;
}
return lastMatch[1];
}
function mapSourcePosition(position: Position): MappedPosition {
const consumer = loadConsumer(position.source);
if (consumer == null) {
return position;
}
const mapped = consumer.originalPositionFor(position);
return mapped;
}
// Parses code generated by FormatEvalOrigin(), a function inside V8:
// https://code.google.com/p/v8/source/browse/trunk/src/messages.js
function mapEvalOrigin(origin: string): string {
// Most eval() calls are in this format
let match = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(origin);
if (match) {
const position = mapSourcePosition({
source: match[2],
line: Number(match[3]),
column: Number(match[4]) - 1
});
const pos = [
position.source,
position.line,
Number(position.column) + 1
].join(":");
return `eval at ${match[1]} (${pos})`;
}
// Parse nested eval() calls using recursion
match = /^eval at ([^(]+) \((.+)\)$/.exec(origin);
if (match) {
return `eval at ${match[1]} (${mapEvalOrigin(match[2])})`;
}
// Make sure we still return useful information if we didn't find anything
return origin;
}

8
v8worker2.d.ts vendored
View file

@ -1,8 +0,0 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
declare namespace V8Worker2 {
function print(...args: any[]): void;
type RecvCallback = (ab: ArrayBuffer) => void;
function recv(cb: RecvCallback): void;
function send(ab: ArrayBuffer): null | ArrayBuffer;
}

3846
yarn.lock

File diff suppressed because it is too large Load diff