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

Merge branch 'deno2'

This commit is contained in:
Ryan Dahl 2018-06-13 15:01:21 +02:00
commit 5c7ba22f22
33 changed files with 6811 additions and 2 deletions

4
.gitignore vendored
View file

@ -4,5 +4,5 @@ dist/
deno
assets.go
msg.pb.go
msg.pb.js
msg.pb.d.ts
/msg.pb.js
/msg.pb.d.ts

37
deno2/.gclient Normal file
View file

@ -0,0 +1,37 @@
solutions = [{
'url': 'https://chromium.googlesource.com/v8/v8.git@2530a044126ae6a1d3dff0d8c61999762847d9f0',
'custom_vars': {
'build_for_node': True
},
'name': 'v8',
'deps_file': 'DEPS',
'custom_deps': {
'v8/third_party/catapult': None,
'v8/third_party/colorama/src': None,
'v8/testing/gmock': None,
'v8/tools/swarming_client': None,
'v8/third_party/instrumented_libraries': None,
'v8/third_party/android_tools': None,
'v8/test/wasm-js': None,
'v8/test/benchmarks/data': None,
'v8/test/mozilla/data': None,
'v8/third_party/icu': None,
'v8/test/test262/data': None,
'v8/test/test262/harness': None,
'v8/tools/luci-go': None
}
}, {
'url': 'https://github.com/ry/protobuf_chromium.git@e62249df45c2a0a9c38e4017e8ab604020b986c5',
'name': 'third_party/protobuf',
'deps_file': 'DEPS'
}, {
'url':
'https://chromium.googlesource.com/chromium/src/tools/protoc_wrapper@9af82fef8cb9ca3ccc13e2ed958f58f2c21f449b',
'name':
'tools/protoc_wrapper'
}, {
'url':
'https://chromium.googlesource.com/chromium/src/third_party/zlib@39b4a6260702da4c089eca57136abf40a39667e9',
'name':
'third_party/zlib'
}]

8
deno2/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
out/
js/.cache/
js/node_modules/
v8/
tools/protoc_wrapper/
third_party/protobuf/
third_party/zlib/
.gclient_entries

61
deno2/.gn Normal file
View file

@ -0,0 +1,61 @@
# This file is used by the GN meta build system to find the root of the source
# tree and to set startup options. For documentation on the values set in this
# file, run "gn help dotfile" at the command line.
import("//v8/build/dotfile_settings.gni")
# The location of the build configuration file.
buildconfig = "//v8/build/config/BUILDCONFIG.gn"
# The secondary source root is a parallel directory tree where
# GN build files are placed when they can not be placed directly
# in the source tree, e.g. for third party source trees.
secondary_source = "//v8/"
# These are the targets to check headers for by default. The files in targets
# matching these patterns (see "gn help label_pattern" for format) will have
# their includes checked for proper dependencies when you run either
# "gn check" or "gn gen --check".
check_targets = []
# These are the list of GN files that run exec_script. This whitelist exists
# to force additional review for new uses of exec_script, which is strongly
# discouraged except for gypi_to_gn calls.
exec_script_whitelist = build_dotfile_settings.exec_script_whitelist + []
default_args = {
# Default to release builds for this project.
is_component_build = false
is_debug = false
libcpp_is_static = false
symbol_level = 1
treat_warnings_as_errors = false
use_custom_libcxx = false
use_sysroot = false
v8_deprecation_warnings = false
#v8_embedder_string = ""
v8_enable_gdbjit = false
v8_enable_i18n_support = false
v8_enable_test_features = false
v8_experimental_extra_library_files = []
v8_extra_library_files = []
v8_imminent_deprecation_warnings = false
v8_monolithic = false
v8_static_library = false
v8_target_cpu = "x64"
v8_untrusted_code_mitigations = false
# This tells V8 to write out/Default/gen/v8/snapshot.bin
# Which we can use to build our own snapshot.
v8_use_external_startup_data = true
v8_use_snapshot = true
# Snapshot the dist/main.js bundle into V8.
# Is ".gn" really the most appropriate place to specify this important
# value? This is how they do it in Chrome.
# https://cs.chromium.org/chromium/src/.gn?l=37&rcl=f1c8c3cf8bd4a63da6433ee67e2ff5ecbbdb4316
# "$target_gen_dir/main.js"
#]
}

216
deno2/BUILD.gn Normal file
View file

@ -0,0 +1,216 @@
import("//third_party/protobuf/proto_library.gni")
import("//v8/gni/v8.gni")
import("//v8/snapshot_toolchain.gni")
executable("deno") {
sources = [
"main.cc",
]
deps = [
":libdeno",
]
}
executable("mock_runtime_test") {
testonly = true
sources = [
"from_snapshot.cc",
"mock_runtime_test.cc",
]
deps = [
":create_snapshot_mock_runtime",
":deno_nosnapshot",
"//testing/gtest:gtest",
]
include_dirs = [ target_gen_dir ]
defines = [ "DENO_MOCK_RUNTIME" ]
}
component("libdeno") {
sources = [
"from_snapshot.cc",
]
deps = [
":create_snapshot_deno",
":deno_nosnapshot",
]
include_dirs = [ target_gen_dir ]
}
source_set("deno_nosnapshot") {
sources = [
"deno.cc",
"deno_internal.h",
"include/deno.h",
]
include_dirs = [ "include/" ]
deps = [
":msg_proto",
"v8:v8",
"v8:v8_libbase",
"v8:v8_libplatform",
"v8:v8_libsampler",
]
}
executable("snapshot_creator") {
sources = [
"snapshot_creator.cc",
]
deps = [
":deno_nosnapshot",
]
}
proto_library("msg_proto") {
sources = [
"msg.proto",
]
}
template("run_node") {
action(target_name) {
forward_variables_from(invoker, "*")
script = "js/run_node.py"
}
}
run_node("bundle") {
out_dir = "$target_gen_dir/bundle/"
sources = [
"$target_gen_dir/tsc_dist/main.js", # Not real input. See run_tsc comment.
"js/main.ts",
]
outputs = [
out_dir + "main.js",
]
deps = [
":run_tsc",
]
args = [
"./node_modules/parcel-bundler/bin/cli.js",
"build",
"--no-minify",
"--out-dir",
rebase_path(out_dir, root_build_dir),
rebase_path("js/main.ts", root_build_dir),
]
}
# Due to bugs in Parcel we must run TSC independently in order to catch errors.
# https://github.com/parcel-bundler/parcel/issues/954
run_node("run_tsc") {
main = "js/main.ts"
tsconfig = "js/tsconfig.json"
out_dir = "$target_gen_dir/tsc_dist/"
sources = [
"js/msg.pb.d.ts",
"js/msg.pb.js",
main,
tsconfig,
]
outputs = [
out_dir + "/main.js",
out_dir + "/main.map",
]
deps = [
":protobufjs",
]
args = [
"./node_modules/typescript/bin/tsc",
"--project",
rebase_path(tsconfig, root_build_dir),
"--outDir",
rebase_path(out_dir, root_build_dir),
]
}
# Generates protobufjs code.
# TODO(ry) Ideally protobufjs output files should be written into
# target_gen_dir, but its difficult to get this working in a way that the
# bundler can resolve their location. (The bundler does not support NODE_PATH?)
# Therefore this hack: write the generated msg.pb.js and msg.pb.d.ts outputs
# into the js/ folder, and we check them into the repo. Hopefully this hack can
# be removed at some point. If msg.proto is changed, commit changes to the
# generated JS files. The stamp file is just to make gn work.
action("protobufjs") {
script = "js/pbjs_hack.py"
sources = [
"msg.proto",
]
outputs = [
"$target_gen_dir/pbjs_hack.stamp",
]
args = [
rebase_path(sources[0], root_build_dir),
rebase_path(outputs[0], root_build_dir),
]
}
# Template to generate different V8 snapshots based on different runtime flags.
# Can be invoked with run_mksnapshot(<name>). The target will resolve to
# run_mksnapshot_<name>. If <name> is "default", no file suffixes will be used.
# Otherwise files are suffixed, e.g. embedded_<name>.cc and
# snapshot_blob_<name>.bin.
#
# The template exposes the variables:
# args: additional flags for mksnapshots
# embedded_suffix: a camel case suffix for method names in the embedded
# snapshot.
template("create_snapshot") {
name = target_name
suffix = "_$name"
action("create_snapshot_" + name) {
forward_variables_from(invoker,
[
"testonly",
"deps",
])
visibility = [ ":*" ] # Only targets in this file can depend on this.
deps += [ ":snapshot_creator" ]
script = "v8/tools/run.py"
data = []
exe = rebase_path(get_label_info(":snapshot_creator", "root_out_dir") +
"/snapshot_creator")
natives_in_bin = "$root_out_dir/natives_blob.bin"
snapshot_in_bin = "$root_out_dir/snapshot_blob.bin"
natives_out_cc = "$target_gen_dir/natives${suffix}.cc"
snapshot_out_cc = "$target_gen_dir/snapshot${suffix}.cc"
sources = [
invoker.js,
]
outputs = [
natives_out_cc,
snapshot_out_cc,
]
args = [
exe,
rebase_path(invoker.js, root_build_dir),
rebase_path(natives_in_bin, root_build_dir),
rebase_path(snapshot_in_bin, root_build_dir),
rebase_path(natives_out_cc, root_build_dir),
rebase_path(snapshot_out_cc, root_build_dir),
]
# To debug snapshotting problems:
# args += ["--trace-serializer"]
data = [
invoker.js,
]
}
}
# Generates $target_gen_dir/snapshot_deno.cc
create_snapshot("deno") {
js = "$target_gen_dir/bundle/main.js"
deps = [
":bundle",
]
}
# Generates $target_gen_dir/snapshot_mock_runtime.cc
create_snapshot("mock_runtime") {
testonly = true
js = "js/mock_runtime.js"
deps = []
}

68
deno2/README.md Normal file
View file

@ -0,0 +1,68 @@
# Deno Prototype 2
## Status
This code is a rewrite of the unprivileged 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 unprivileged 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

1
deno2/build Symbolic link
View file

@ -0,0 +1 @@
v8/build

340
deno2/deno.cc Normal file
View file

@ -0,0 +1,340 @@
/*
Copyright 2018 Ryan Dahl <ry@tinyclouds.org>. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8.h"
#include "./deno_internal.h"
#include "include/deno.h"
#define CHECK(x) assert(x) // TODO(ry) use V8's CHECK.
namespace deno {
// Extracts a C string from a v8::V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) {
return *value ? *value : "<string conversion failed>";
}
static inline v8::Local<v8::String> v8_str(const char* x) {
return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x,
v8::NewStringType::kNormal)
.ToLocalChecked();
}
void HandleException(v8::Local<v8::Context> context,
v8::Local<v8::Value> exception) {
auto* isolate = context->GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(context);
auto message = v8::Exception::CreateMessage(isolate, exception);
auto onerrorStr = v8::String::NewFromUtf8(isolate, "onerror");
auto onerror = context->Global()->Get(onerrorStr);
if (onerror->IsFunction()) {
auto func = v8::Local<v8::Function>::Cast(onerror);
v8::Local<v8::Value> args[5];
auto origin = message->GetScriptOrigin();
args[0] = exception->ToString();
args[1] = message->GetScriptResourceName();
args[2] = origin.ResourceLineOffset();
args[3] = origin.ResourceColumnOffset();
args[4] = exception;
func->Call(context->Global(), 5, args);
/* message, source, lineno, colno, error */
} else {
v8::String::Utf8Value exceptionStr(isolate, exception);
printf("Unhandled Exception %s\n", ToCString(exceptionStr));
message->PrintCurrentStackTrace(isolate, stdout);
}
}
/*
bool AbortOnUncaughtExceptionCallback(v8::Isolate* isolate) {
return true;
}
void MessageCallback2(Local<Message> message, v8::Local<v8::Value> data) {
printf("MessageCallback2\n\n");
}
void FatalErrorCallback2(const char* location, const char* message) {
printf("FatalErrorCallback2\n");
}
*/
void ExitOnPromiseRejectCallback(
v8::PromiseRejectMessage promise_reject_message) {
auto* isolate = v8::Isolate::GetCurrent();
Deno* d = static_cast<Deno*>(isolate->GetData(0));
assert(d->isolate == isolate);
v8::HandleScope handle_scope(d->isolate);
auto exception = promise_reject_message.GetValue();
auto context = d->context.Get(d->isolate);
HandleException(context, exception);
}
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
assert(args.Length() == 1);
auto* isolate = args.GetIsolate();
v8::HandleScope handle_scope(isolate);
v8::String::Utf8Value str(isolate, args[0]);
const char* cstr = ToCString(str);
printf("%s\n", cstr);
fflush(stdout);
}
// Sets the sub callback.
void Sub(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0));
assert(d->isolate == isolate);
v8::HandleScope handle_scope(isolate);
if (!d->sub.IsEmpty()) {
isolate->ThrowException(v8_str("denoSub already called."));
return;
}
v8::Local<v8::Value> v = args[0];
assert(v->IsFunction());
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
d->sub.Reset(isolate, func);
}
void Pub(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
Deno* d = static_cast<Deno*>(isolate->GetData(0));
assert(d->isolate == isolate);
v8::Locker locker(d->isolate);
v8::EscapableHandleScope handle_scope(isolate);
assert(args.Length() == 2);
v8::Local<v8::Value> channel_v = args[0];
assert(channel_v->IsString());
v8::String::Utf8Value channel_vstr(isolate, channel_v);
const char* channel = *channel_vstr;
v8::Local<v8::Value> ab_v = args[1];
assert(ab_v->IsArrayBuffer());
auto ab = v8::Local<v8::ArrayBuffer>::Cast(ab_v);
auto contents = ab->GetContents();
// data is only a valid pointer until the end of this call.
const char* data =
const_cast<const char*>(reinterpret_cast<char*>(contents.Data()));
deno_buf buf{data, contents.ByteLength()};
auto retbuf = d->cb(d, channel, buf);
if (retbuf.data) {
// TODO(ry) Support zero-copy.
auto ab = v8::ArrayBuffer::New(d->isolate, retbuf.len);
memcpy(ab->GetContents().Data(), retbuf.data, retbuf.len);
args.GetReturnValue().Set(handle_scope.Escape(ab));
} else {
args.GetReturnValue().Set(v8::Null(d->isolate));
}
}
bool Execute(v8::Local<v8::Context> context, const char* js_filename,
const char* js_source) {
auto* isolate = context->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(isolate);
auto name = v8_str(js_filename);
auto source = v8_str(js_source);
v8::ScriptOrigin origin(name);
auto script = v8::Script::Compile(context, source, &origin);
if (script.IsEmpty()) {
assert(try_catch.HasCaught());
HandleException(context, try_catch.Exception());
return false;
}
auto result = script.ToLocalChecked()->Run(context);
if (result.IsEmpty()) {
assert(try_catch.HasCaught());
HandleException(context, try_catch.Exception());
return false;
}
return true;
}
v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index,
void* data) {
assert(data == nullptr); // TODO(ry) pass Deno* object here.
InternalFieldData* embedder_field = static_cast<InternalFieldData*>(
holder->GetAlignedPointerFromInternalField(index));
if (embedder_field == nullptr) return {nullptr, 0};
int size = sizeof(*embedder_field);
char* payload = new char[size];
// We simply use memcpy to serialize the content.
memcpy(payload, embedder_field, size);
return {payload, size};
}
v8::StartupData MakeSnapshot(v8::StartupData* prev_natives_blob,
v8::StartupData* prev_snapshot_blob,
const char* js_filename, const char* js_source) {
v8::V8::SetNativesDataBlob(prev_natives_blob);
v8::V8::SetSnapshotDataBlob(prev_snapshot_blob);
auto* creator = new v8::SnapshotCreator(external_references);
auto* isolate = creator->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
{
v8::HandleScope handle_scope(isolate);
auto context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
auto global = context->Global();
// TODO(ry) Add a global namespace object "deno" and move print, sub, and
// pub inside that object.
auto print_tmpl = v8::FunctionTemplate::New(isolate, Print);
auto print_val = print_tmpl->GetFunction(context).ToLocalChecked();
CHECK(
global->Set(context, deno::v8_str("denoPrint"), print_val).FromJust());
auto sub_tmpl = v8::FunctionTemplate::New(isolate, Sub);
auto sub_val = sub_tmpl->GetFunction(context).ToLocalChecked();
CHECK(global->Set(context, deno::v8_str("denoSub"), sub_val).FromJust());
auto pub_tmpl = v8::FunctionTemplate::New(isolate, Pub);
auto pub_val = pub_tmpl->GetFunction(context).ToLocalChecked();
CHECK(global->Set(context, deno::v8_str("denoPub"), pub_val).FromJust());
bool r = Execute(context, js_filename, js_source);
CHECK(r);
creator->SetDefaultContext(context, v8::SerializeInternalFieldsCallback(
SerializeInternalFields, nullptr));
}
auto snapshot_blob =
creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
return snapshot_blob;
}
void AddIsolate(Deno* d, v8::Isolate* isolate) {
d->isolate = isolate;
// Leaving this code here because it will probably be useful later on, but
// disabling it now as I haven't got tests for the desired behavior.
// d->isolate->SetCaptureStackTraceForUncaughtExceptions(true);
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
// d->isolate->AddMessageListener(MessageCallback2);
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
d->isolate->SetPromiseRejectCallback(deno::ExitOnPromiseRejectCallback);
d->isolate->SetData(0, d);
}
} // namespace deno
extern "C" {
void deno_init() {
// v8::V8::InitializeICUDefaultLocation(argv[0]);
// v8::V8::InitializeExternalStartupData(argv[0]);
auto* p = v8::platform::CreateDefaultPlatform();
v8::V8::InitializePlatform(p);
v8::V8::Initialize();
}
const char* deno_v8_version() { return v8::V8::GetVersion(); }
void deno_set_flags(int* argc, char** argv) {
v8::V8::SetFlagsFromCommandLine(argc, argv, true);
}
const char* deno_last_exception(Deno* d) { return d->last_exception.c_str(); }
bool deno_execute(Deno* d, const char* js_filename, const char* js_source) {
auto* isolate = d->isolate;
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
auto context = d->context.Get(d->isolate);
return deno::Execute(context, js_filename, js_source);
}
bool deno_pub(Deno* d, const char* channel, deno_buf buf) {
v8::Locker locker(d->isolate);
v8::Isolate::Scope isolate_scope(d->isolate);
v8::HandleScope handle_scope(d->isolate);
auto context = d->context.Get(d->isolate);
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch(d->isolate);
auto sub = d->sub.Get(d->isolate);
if (sub.IsEmpty()) {
d->last_exception = "deno_sub has not been called.";
return false;
}
// TODO(ry) support zero-copy.
auto ab = v8::ArrayBuffer::New(d->isolate, buf.len);
memcpy(ab->GetContents().Data(), buf.data, buf.len);
v8::Local<v8::Value> args[2];
args[0] = deno::v8_str(channel);
args[1] = ab;
sub->Call(context->Global(), 1, args);
if (try_catch.HasCaught()) {
deno::HandleException(context, try_catch.Exception());
return false;
}
return true;
}
void deno_delete(Deno* d) {
d->isolate->Dispose();
delete d;
}
void deno_terminate_execution(Deno* d) { d->isolate->TerminateExecution(); }
} // extern "C"

44
deno2/deno_internal.h Normal file
View file

@ -0,0 +1,44 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
#ifndef DENO_INTERNAL_H_
#define DENO_INTERNAL_H_
#include <string>
#include "include/deno.h"
#include "v8/include/v8.h"
extern "C" {
// deno_s = Wrapped Isolate.
struct deno_s {
v8::Isolate* isolate;
std::string last_exception;
v8::Persistent<v8::Function> sub;
v8::Persistent<v8::Context> context;
deno_sub_cb cb;
void* data;
};
}
namespace deno {
struct InternalFieldData {
uint32_t data;
};
void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
void Sub(const v8::FunctionCallbackInfo<v8::Value>& args);
void Pub(const v8::FunctionCallbackInfo<v8::Value>& args);
static intptr_t external_references[] = {reinterpret_cast<intptr_t>(Print),
reinterpret_cast<intptr_t>(Sub),
reinterpret_cast<intptr_t>(Pub), 0};
Deno* NewFromSnapshot(void* data, deno_sub_cb cb);
v8::StartupData MakeSnapshot(v8::StartupData* prev_natives_blob,
v8::StartupData* prev_snapshot_blob,
const char* js_filename, const char* js_source);
void AddIsolate(Deno* d, v8::Isolate* isolate);
} // namespace deno
#endif // DENO_INTERNAL_H_

78
deno2/from_snapshot.cc Normal file
View file

@ -0,0 +1,78 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "v8/include/v8.h"
#include "./deno_internal.h"
#include "include/deno.h"
namespace deno {
#ifdef DENO_MOCK_RUNTIME
#include "natives_mock_runtime.cc"
#include "snapshot_mock_runtime.cc"
#else
#include "natives_deno.cc"
#include "snapshot_deno.cc"
#endif
std::vector<InternalFieldData*> deserialized_data;
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
v8::StartupData payload, void* data) {
assert(data == nullptr); // TODO(ry) pass Deno* object here.
if (payload.raw_size == 0) {
holder->SetAlignedPointerInInternalField(index, nullptr);
return;
}
InternalFieldData* embedder_field = new InternalFieldData{0};
memcpy(embedder_field, payload.data, payload.raw_size);
holder->SetAlignedPointerInInternalField(index, embedder_field);
deserialized_data.push_back(embedder_field);
}
Deno* NewFromSnapshot(void* data, deno_sub_cb cb) {
auto natives_blob = *StartupBlob_natives();
auto snapshot_blob = *StartupBlob_snapshot();
v8::V8::SetNativesDataBlob(&natives_blob);
v8::V8::SetSnapshotDataBlob(&snapshot_blob);
v8::DeserializeInternalFieldsCallback(DeserializeInternalFields, nullptr);
Deno* d = new Deno;
d->cb = cb;
d->data = data;
v8::Isolate::CreateParams params;
params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
params.external_references = external_references;
v8::Isolate* isolate = v8::Isolate::New(params);
AddIsolate(d, isolate);
v8::Locker locker(isolate);
v8::Isolate::Scope isolate_scope(isolate);
{
v8::HandleScope handle_scope(isolate);
auto context =
v8::Context::New(isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
v8::MaybeLocal<v8::Value>(),
v8::DeserializeInternalFieldsCallback(
DeserializeInternalFields, nullptr));
d->context.Reset(d->isolate, context);
}
return d;
}
} // namespace deno
extern "C" {
Deno* deno_new(void* data, deno_sub_cb cb) {
return deno::NewFromSnapshot(data, cb);
}
}

47
deno2/include/deno.h Normal file
View file

@ -0,0 +1,47 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
#ifndef INCLUDE_DENO_H_
#define INCLUDE_DENO_H_
// Neither Rust nor Go support calling directly into C++ functions, therefore
// the public interface to libdeno is done in C.
#ifdef __cplusplus
extern "C" {
#endif
// Data that gets transmitted.
typedef struct {
const char* data;
size_t len;
} deno_buf;
struct deno_s;
typedef struct deno_s Deno;
// A callback to receive a message from deno_pub javascript call.
// buf is valid only for the lifetime of the call.
// The returned deno_buf is returned from deno_pub in javascript.
typedef deno_buf (*deno_sub_cb)(Deno* d, const char* channel, deno_buf buf);
void deno_init();
const char* deno_v8_version();
void deno_set_flags(int* argc, char** argv);
Deno* deno_new(void* data, deno_sub_cb cb);
void deno_delete(Deno* d);
// Returns false on error.
// Get error text with deno_last_exception().
bool deno_execute(Deno* d, const char* js_filename, const char* js_source);
// Routes message to the javascript callback set with deno_sub(). A false return
// value indicates error. Check deno_last_exception() for exception text.
bool deno_pub(Deno* d, const char* channel, deno_buf buf);
const char* deno_last_exception(Deno* d);
void deno_terminate_execution(Deno* d);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // INCLUDE_DENO_H_

6
deno2/js/deno.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
type MessageCallback = (msg: ArrayBuffer) => void;
declare function denoSub(channel: string, cb: MessageCallback): void;
declare function denoPub(channel: string, msg: ArrayBuffer): null | ArrayBuffer;
declare function denoPrint(x: string): void;

14
deno2/js/main.ts Normal file
View file

@ -0,0 +1,14 @@
/// <reference path="deno.d.ts" />
import { main as pb } from "./msg.pb";
import * as ts from "typescript";
const globalEval = eval;
const window = globalEval("this");
window["denoMain"] = () => {
denoPrint("Hello world");
const msg = pb.Msg.fromObject({});
denoPrint(`msg.command: ${msg.command}`);
denoPrint(`ts.version: ${ts.version}`);
denoPrint("Hello world from foo");
return "foo";
};

69
deno2/js/mock_runtime.js Normal file
View file

@ -0,0 +1,69 @@
// A simple runtime that doesn't involve typescript or protobufs to test
// libdeno. Invoked by mock_runtime_test.cc
const window = eval("this");
function assert(cond) {
if (!cond) throw Error("mock_runtime.js assert failed");
}
function typedArrayToArrayBuffer(ta) {
return ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength);
}
function CanCallFunction() {
denoPrint("Hello world from foo");
return "foo";
}
// This object is created to test snapshotting.
// See DeserializeInternalFieldsCallback and SerializeInternalFieldsCallback.
const snapshotted = new Uint8Array([1, 3, 3, 7]);
function TypedArraySnapshots() {
assert(snapshotted[0] === 1);
assert(snapshotted[1] === 3);
assert(snapshotted[2] === 3);
assert(snapshotted[3] === 7);
}
function PubSuccess() {
denoSub((channel, msg) => {
assert(channel === "PubSuccess");
denoPrint("PubSuccess: ok");
});
}
function PubByteLength() {
denoSub((channel, msg) => {
assert(channel === "PubByteLength");
assert(msg instanceof ArrayBuffer);
assert(msg.byteLength === 3);
});
}
function SubReturnEmpty() {
const ui8 = new Uint8Array("abc".split("").map(c => c.charCodeAt(0)));
const ab = typedArrayToArrayBuffer(ui8);
let r = denoPub("SubReturnEmpty", ab);
assert(r == null);
r = denoPub("SubReturnEmpty", ab);
assert(r == null);
}
function SubReturnBar() {
const ui8 = new Uint8Array("abc".split("").map(c => c.charCodeAt(0)));
const ab = typedArrayToArrayBuffer(ui8);
const r = denoPub("SubReturnBar", ab);
assert(r instanceof ArrayBuffer);
assert(r.byteLength === 3);
const rui8 = new Uint8Array(r);
const rstr = String.fromCharCode(...rui8);
assert(rstr === "bar");
}
function DoubleSubFails() {
// denoSub is an internal function and should only be called once from the
// runtime.
denoSub((channel, msg) => assert(false));
denoSub((channel, msg) => assert(false));
}

410
deno2/js/msg.pb.d.ts vendored Normal file
View file

@ -0,0 +1,410 @@
import * as $protobuf from "protobufjs";
/** Namespace main. */
export namespace main {
/** Properties of a BaseMsg. */
interface IBaseMsg {
/** BaseMsg channel */
channel?: (string|null);
/** BaseMsg payload */
payload?: (Uint8Array|null);
}
/** Represents a BaseMsg. */
class BaseMsg implements IBaseMsg {
/**
* Constructs a new BaseMsg.
* @param [properties] Properties to set
*/
constructor(properties?: main.IBaseMsg);
/** BaseMsg channel. */
public channel: string;
/** BaseMsg payload. */
public payload: Uint8Array;
/**
* Creates a new BaseMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns BaseMsg instance
*/
public static create(properties?: main.IBaseMsg): main.BaseMsg;
/**
* Encodes the specified BaseMsg message. Does not implicitly {@link main.BaseMsg.verify|verify} messages.
* @param message BaseMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: main.IBaseMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified BaseMsg message, length delimited. Does not implicitly {@link main.BaseMsg.verify|verify} messages.
* @param message BaseMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: main.IBaseMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a BaseMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns BaseMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): main.BaseMsg;
/**
* Decodes a BaseMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns BaseMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): main.BaseMsg;
/**
* Verifies a BaseMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a BaseMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns BaseMsg
*/
public static fromObject(object: { [k: string]: any }): main.BaseMsg;
/**
* Creates a plain object from a BaseMsg message. Also converts values to other types if specified.
* @param message BaseMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: main.BaseMsg, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this BaseMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
}
/** Properties of a Msg. */
interface IMsg {
/** Msg command */
command?: (main.Msg.Command|null);
/** Msg error */
error?: (string|null);
/** Msg startCwd */
startCwd?: (string|null);
/** Msg startArgv */
startArgv?: (string[]|null);
/** Msg startDebugFlag */
startDebugFlag?: (boolean|null);
/** Msg startMainJs */
startMainJs?: (string|null);
/** Msg startMainMap */
startMainMap?: (string|null);
/** Msg codeFetchModuleSpecifier */
codeFetchModuleSpecifier?: (string|null);
/** Msg codeFetchContainingFile */
codeFetchContainingFile?: (string|null);
/** Msg codeFetchResModuleName */
codeFetchResModuleName?: (string|null);
/** Msg codeFetchResFilename */
codeFetchResFilename?: (string|null);
/** Msg codeFetchResSourceCode */
codeFetchResSourceCode?: (string|null);
/** Msg codeFetchResOutputCode */
codeFetchResOutputCode?: (string|null);
/** Msg codeCacheFilename */
codeCacheFilename?: (string|null);
/** Msg codeCacheSourceCode */
codeCacheSourceCode?: (string|null);
/** Msg codeCacheOutputCode */
codeCacheOutputCode?: (string|null);
/** Msg exitCode */
exitCode?: (number|null);
/** Msg timerStartId */
timerStartId?: (number|null);
/** Msg timerStartInterval */
timerStartInterval?: (boolean|null);
/** Msg timerStartDelay */
timerStartDelay?: (number|null);
/** Msg timerReadyId */
timerReadyId?: (number|null);
/** Msg timerReadyDone */
timerReadyDone?: (boolean|null);
/** Msg timerClearId */
timerClearId?: (number|null);
/** Msg fetchReqId */
fetchReqId?: (number|null);
/** Msg fetchReqUrl */
fetchReqUrl?: (string|null);
/** Msg fetchResId */
fetchResId?: (number|null);
/** Msg fetchResStatus */
fetchResStatus?: (number|null);
/** Msg fetchResHeaderLine */
fetchResHeaderLine?: (string[]|null);
/** Msg fetchResBody */
fetchResBody?: (Uint8Array|null);
/** Msg readFileSyncFilename */
readFileSyncFilename?: (string|null);
/** Msg readFileSyncData */
readFileSyncData?: (Uint8Array|null);
/** Msg writeFileSyncFilename */
writeFileSyncFilename?: (string|null);
/** Msg writeFileSyncData */
writeFileSyncData?: (Uint8Array|null);
/** Msg writeFileSyncPerm */
writeFileSyncPerm?: (number|null);
}
/** Represents a Msg. */
class Msg implements IMsg {
/**
* Constructs a new Msg.
* @param [properties] Properties to set
*/
constructor(properties?: main.IMsg);
/** Msg command. */
public command: main.Msg.Command;
/** Msg error. */
public error: string;
/** Msg startCwd. */
public startCwd: string;
/** Msg startArgv. */
public startArgv: string[];
/** Msg startDebugFlag. */
public startDebugFlag: boolean;
/** Msg startMainJs. */
public startMainJs: string;
/** Msg startMainMap. */
public startMainMap: string;
/** Msg codeFetchModuleSpecifier. */
public codeFetchModuleSpecifier: string;
/** Msg codeFetchContainingFile. */
public codeFetchContainingFile: string;
/** Msg codeFetchResModuleName. */
public codeFetchResModuleName: string;
/** Msg codeFetchResFilename. */
public codeFetchResFilename: string;
/** Msg codeFetchResSourceCode. */
public codeFetchResSourceCode: string;
/** Msg codeFetchResOutputCode. */
public codeFetchResOutputCode: string;
/** Msg codeCacheFilename. */
public codeCacheFilename: string;
/** Msg codeCacheSourceCode. */
public codeCacheSourceCode: string;
/** Msg codeCacheOutputCode. */
public codeCacheOutputCode: string;
/** Msg exitCode. */
public exitCode: number;
/** Msg timerStartId. */
public timerStartId: number;
/** Msg timerStartInterval. */
public timerStartInterval: boolean;
/** Msg timerStartDelay. */
public timerStartDelay: number;
/** Msg timerReadyId. */
public timerReadyId: number;
/** Msg timerReadyDone. */
public timerReadyDone: boolean;
/** Msg timerClearId. */
public timerClearId: number;
/** Msg fetchReqId. */
public fetchReqId: number;
/** Msg fetchReqUrl. */
public fetchReqUrl: string;
/** Msg fetchResId. */
public fetchResId: number;
/** Msg fetchResStatus. */
public fetchResStatus: number;
/** Msg fetchResHeaderLine. */
public fetchResHeaderLine: string[];
/** Msg fetchResBody. */
public fetchResBody: Uint8Array;
/** Msg readFileSyncFilename. */
public readFileSyncFilename: string;
/** Msg readFileSyncData. */
public readFileSyncData: Uint8Array;
/** Msg writeFileSyncFilename. */
public writeFileSyncFilename: string;
/** Msg writeFileSyncData. */
public writeFileSyncData: Uint8Array;
/** Msg writeFileSyncPerm. */
public writeFileSyncPerm: number;
/**
* Creates a new Msg instance using the specified properties.
* @param [properties] Properties to set
* @returns Msg instance
*/
public static create(properties?: main.IMsg): main.Msg;
/**
* Encodes the specified Msg message. Does not implicitly {@link main.Msg.verify|verify} messages.
* @param message Msg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: main.IMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Encodes the specified Msg message, length delimited. Does not implicitly {@link main.Msg.verify|verify} messages.
* @param message Msg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: main.IMsg, writer?: $protobuf.Writer): $protobuf.Writer;
/**
* Decodes a Msg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns Msg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): main.Msg;
/**
* Decodes a Msg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns Msg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): main.Msg;
/**
* Verifies a Msg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): (string|null);
/**
* Creates a Msg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns Msg
*/
public static fromObject(object: { [k: string]: any }): main.Msg;
/**
* Creates a plain object from a Msg message. Also converts values to other types if specified.
* @param message Msg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(message: main.Msg, options?: $protobuf.IConversionOptions): { [k: string]: any };
/**
* Converts this Msg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any };
}
namespace Msg {
/** Command enum. */
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
}
}
}

1313
deno2/js/msg.pb.js Normal file

File diff suppressed because it is too large Load diff

7
deno2/js/package.json Normal file
View file

@ -0,0 +1,7 @@
{
"devDependencies": {
"parcel-bundler": "^1.8.1",
"protobufjs": "^6.8.6",
"typescript": "^2.9.1"
}
}

54
deno2/js/pbjs_hack.py Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
"""
gn can only run python scripts.
Generates protobufjs code.
"""
import subprocess
import sys
import os
# TODO(ry) Ideally protobufjs output files should be written into
# target_gen_dir, but its difficult to get this working in a way that parcel can
# resolve their location. (Parcel does not support NODE_PATH.) Therefore this
# hack: write the generated msg.pb.js and msg.pb.d.ts outputs into the js/
# folder, and we check them into the repo. Hopefully this hack can be removed at
# some point. If msg.proto is changed, commit changes to the generated JS
# files.
js_path = os.path.dirname(os.path.realpath(__file__))
pbjs_path = os.path.join(js_path, "node_modules", "protobufjs", "bin")
pbjs_bin = os.path.join(pbjs_path, "pbjs")
pbts_bin = os.path.join(pbjs_path, "pbts")
msg_pbjs_out = os.path.join(js_path, "msg.pb.js")
msg_pbts_out = os.path.join(js_path, "msg.pb.d.ts")
assert os.path.exists(pbjs_bin)
assert os.path.exists(pbts_bin)
proto_in = sys.argv[1]
stamp_file = sys.argv[2]
def touch(fname):
if os.path.exists(fname):
os.utime(fname, None)
else:
open(fname, 'a').close()
subprocess.check_call([
"node",
pbjs_bin,
"--target=static-module",
"--wrapper=commonjs",
"--out=" + msg_pbjs_out,
proto_in
])
assert os.path.exists(msg_pbjs_out)
subprocess.check_call([
"node",
pbts_bin,
"--out=" + msg_pbts_out,
msg_pbjs_out
])
assert os.path.exists(msg_pbts_out)
touch(stamp_file)

32
deno2/js/run_node.py Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env python
"""
gn can only run python scripts. This launches a subprocess Node process.
The working dir of this program is out/Debug/ (AKA root_build_dir)
Before running node, we symlink js/node_modules to out/Debug/node_modules.
"""
import subprocess
import sys
import os
def symlink(target, name, target_is_dir=False):
if os.name == "nt":
from ctypes import windll, WinError
CreateSymbolicLinkW = windll.kernel32.CreateSymbolicLinkW
flags = 0x02 # SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
if (target_is_dir):
flags |= 0x01 # SYMBOLIC_LINK_FLAG_DIRECTORY
if not CreateSymbolicLinkW(name.encode('utf-16le'),
target.encode('utf-16le'),
flags):
raise WinError()
else:
os.symlink(target, name)
js_path = os.path.dirname(os.path.realpath(__file__))
node_modules_path = os.path.join(js_path, "node_modules")
if not os.path.exists("node_modules"):
symlink(node_modules_path, "node_modules", True)
args = ["node"] + sys.argv[1:]
sys.exit(subprocess.call(args))

18
deno2/js/tsconfig.json Normal file
View file

@ -0,0 +1,18 @@
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"noImplicitAny": true,
"sourceMap": true,
"removeComments": true,
"preserveConstEnums": true,
"target": "es2017",
"noImplicitReturns": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"allowUnreachableCode": false,
"experimentalDecorators": true
},
"include": ["**/*.ts", "**/*.js"],
"exclude": ["mock_runtime.js", "node_modules"]
}

3629
deno2/js/yarn.lock Normal file

File diff suppressed because it is too large Load diff

19
deno2/main.cc Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "include/deno.h"
int main(int argc, char** argv) {
deno_init();
Deno* d = deno_new(NULL, NULL);
bool r = deno_execute(d, "deno_main.js", "denoMain();");
if (!r) {
printf("Error! %s\n", deno_last_exception(d));
exit(1);
}
deno_delete(d);
}

View file

@ -0,0 +1,98 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
#include "testing/gtest/include/gtest/gtest.h"
#include "include/deno.h"
TEST(MockRuntimeTest, InitializesCorrectly) {
Deno* d = deno_new(NULL, NULL);
EXPECT_TRUE(deno_execute(d, "a.js", "1 + 2"));
deno_delete(d);
}
TEST(MockRuntimeTest, CanCallFunction) {
Deno* d = deno_new(NULL, NULL);
EXPECT_TRUE(deno_execute(d, "a.js",
"if (CanCallFunction() != 'foo') throw Error();"));
deno_delete(d);
}
TEST(MockRuntimeTest, ErrorsCorrectly) {
Deno* d = deno_new(NULL, NULL);
EXPECT_FALSE(deno_execute(d, "a.js", "throw Error()"));
deno_delete(d);
}
deno_buf strbuf(const char* str) { return deno_buf{str, strlen(str)}; }
TEST(MockRuntimeTest, PubSuccess) {
Deno* d = deno_new(NULL, NULL);
EXPECT_TRUE(deno_execute(d, "a.js", "PubSuccess()"));
EXPECT_TRUE(deno_pub(d, "PubSuccess", strbuf("abc")));
deno_delete(d);
}
TEST(MockRuntimeTest, PubByteLength) {
Deno* d = deno_new(NULL, NULL);
EXPECT_TRUE(deno_execute(d, "a.js", "PubByteLength()"));
// We pub the wrong sized message, it should throw.
EXPECT_FALSE(deno_pub(d, "PubByteLength", strbuf("abcd")));
deno_delete(d);
}
TEST(MockRuntimeTest, PubNoCallback) {
Deno* d = deno_new(NULL, NULL);
// We didn't call deno_sub(), pubing should fail.
EXPECT_FALSE(deno_pub(d, "PubNoCallback", strbuf("abc")));
deno_delete(d);
}
TEST(MockRuntimeTest, SubReturnEmpty) {
static int count = 0;
Deno* d = deno_new(NULL, [](auto _, auto channel, auto buf) {
count++;
EXPECT_STREQ(channel, "SubReturnEmpty");
EXPECT_EQ(static_cast<size_t>(3), buf.len);
EXPECT_EQ(buf.data[0], 'a');
EXPECT_EQ(buf.data[1], 'b');
EXPECT_EQ(buf.data[2], 'c');
return deno_buf{nullptr, 0};
});
EXPECT_TRUE(deno_execute(d, "a.js", "SubReturnEmpty()"));
EXPECT_EQ(count, 2);
deno_delete(d);
}
TEST(MockRuntimeTest, SubReturnBar) {
static int count = 0;
Deno* d = deno_new(NULL, [](auto _, auto channel, auto buf) {
count++;
EXPECT_STREQ(channel, "SubReturnBar");
EXPECT_EQ(static_cast<size_t>(3), buf.len);
EXPECT_EQ(buf.data[0], 'a');
EXPECT_EQ(buf.data[1], 'b');
EXPECT_EQ(buf.data[2], 'c');
return strbuf("bar");
});
EXPECT_TRUE(deno_execute(d, "a.js", "SubReturnBar()"));
EXPECT_EQ(count, 1);
deno_delete(d);
}
TEST(MockRuntimeTest, DoubleSubFails) {
Deno* d = deno_new(NULL, NULL);
EXPECT_FALSE(deno_execute(d, "a.js", "DoubleSubFails()"));
deno_delete(d);
}
TEST(MockRuntimeTest, TypedArraySnapshots) {
Deno* d = deno_new(NULL, NULL);
EXPECT_TRUE(deno_execute(d, "a.js", "TypedArraySnapshots()"));
deno_delete(d);
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
deno_init();
return RUN_ALL_TESTS();
}

1
deno2/msg.proto Symbolic link
View file

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

148
deno2/snapshot_creator.cc Normal file
View file

@ -0,0 +1,148 @@
// Copyright 2018 Ryan Dahl <ry@tinyclouds.org>
// All rights reserved. MIT License.
// Hint: --trace_serializer is a useful debugging flag.
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iterator>
#include <vector>
#include "v8/include/v8.h"
#include "./deno_internal.h"
#include "include/deno.h"
class StartupDataCppWriter {
public:
StartupDataCppWriter(const char* name, const char* filename,
v8::StartupData sd)
: name_(name),
filename_(filename),
sd_(sd),
file_(filename_, std::ios::binary) {}
void Write() {
WritePrefix();
WriteData();
WriteSuffix();
file_.close();
if (file_.bad()) {
printf("Unable to open file \"%s\" for writing.\n", filename_);
exit(1);
}
// printf("Wrote %s %d %s \n", name_, sd_.raw_size, filename_);
}
private:
void WritePrefix() {
file_ << "// Autogenerated snapshot file. Do not edit.\n\n";
file_ << "#include \"v8/include/v8.h\"\n\n";
}
void WriteSuffix() {
char buffer[500];
snprintf(buffer, sizeof(buffer),
"const v8::StartupData* StartupBlob_%s() {\n", name_);
file_ << buffer;
snprintf(buffer, sizeof(buffer), " return &%s_blob;\n", name_);
file_ << buffer;
file_ << "}\n\n";
}
void WriteBinaryContentsAsCArray() {
char buffer[5];
for (int i = 0; i < sd_.raw_size; i++) {
if ((i & 0x1F) == 0x1F) file_ << "\n";
if (i > 0) file_ << ",";
snprintf(buffer, sizeof(buffer), "%u",
static_cast<unsigned char>(sd_.data[i]));
file_ << buffer;
}
file_ << "\n";
}
void WriteData() {
char buffer[500];
snprintf(buffer, sizeof(buffer), "static const char %s_blob_data[] = {\n",
name_);
file_ << buffer;
WriteBinaryContentsAsCArray();
file_ << "};\n";
snprintf(buffer, sizeof(buffer), "static const int %s_blob_size = %d;\n",
name_, sd_.raw_size);
file_ << buffer;
snprintf(buffer, sizeof(buffer), "static const v8::StartupData %s_blob =\n",
name_);
file_ << buffer;
snprintf(buffer, sizeof(buffer),
"{ (const char*) %s_blob_data, %s_blob_size };\n", name_, name_);
file_ << buffer;
}
const char* name_;
const char* filename_;
v8::StartupData sd_;
std::ofstream file_;
};
// Caller must free returned value.
static v8::StartupData ReadFile(const char* fn) {
std::ifstream input(fn, std::ios::binary);
if (input.bad()) {
printf("Error reading %s\n", fn);
exit(1);
}
// Note the allocated buffer is intentionally not freed in this program.
// It may show up as a memory leak some day, but other than that it's
// harmless.
auto* buffer = new std::vector<char>((std::istreambuf_iterator<char>(input)),
(std::istreambuf_iterator<char>()));
v8::StartupData sd;
sd.data = buffer->data();
sd.raw_size = static_cast<int>(buffer->size());
if (input.bad()) {
printf("Error reading %s\n", fn);
exit(1);
}
return sd;
}
void WriteFile(const char* fn, v8::StartupData startup_data) {
std::ofstream output(fn, std::ios::binary);
output.write(startup_data.data, startup_data.raw_size);
output.close();
if (output.bad()) {
printf("Error writing %s\n", fn);
exit(1);
}
}
int main(int argc, char** argv) {
// The only documentation for this programs arguments is right here.
const char* js_fn = argv[1];
const char* natives_in_bin = argv[2];
const char* snapshot_in_bin = argv[3];
const char* natives_out_cc = argv[4];
const char* snapshot_out_cc = argv[5];
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
auto js_data = ReadFile(js_fn);
// Ensure js_source is null-terminated.
std::string js_source(js_data.data, js_data.raw_size);
auto natives_blob = ReadFile(natives_in_bin);
auto snapshot_in_blob = ReadFile(snapshot_in_bin);
deno_init();
auto snapshot_blob =
deno::MakeSnapshot(&natives_blob, &snapshot_in_blob, js_fn, js_source.c_str());
StartupDataCppWriter nativesWriter("natives", natives_out_cc, natives_blob);
nativesWriter.Write();
StartupDataCppWriter snapshotWriter("snapshot", snapshot_out_cc,
snapshot_blob);
snapshotWriter.Write();
}

1
deno2/testing Symbolic link
View file

@ -0,0 +1 @@
v8/testing

1
deno2/third_party/googletest vendored Symbolic link
View file

@ -0,0 +1 @@
../v8/third_party/googletest/

1
deno2/third_party/jinja2 vendored Symbolic link
View file

@ -0,0 +1 @@
../v8/third_party/jinja2/

1
deno2/third_party/llvm-build vendored Symbolic link
View file

@ -0,0 +1 @@
../v8/third_party/llvm-build/

1
deno2/third_party/markupsafe vendored Symbolic link
View file

@ -0,0 +1 @@
../v8/third_party/markupsafe/

67
deno2/tools/build.py Executable file
View file

@ -0,0 +1,67 @@
#!/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()

15
deno2/tools/format.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/sh
cd `dirname "$0"`/..
clang-format -i -style Google *.cc *.h include/*.h
gn format BUILD.gn
gn format .gn
yapf -i tools/*.py
prettier --write \
js/deno.d.ts \
js/main.ts \
js/mock_runtime.js \
js/package.json \
js/tsconfig.json
# Do not format these.
# js/msg.pb.js
# js/msg.pb.d.ts

4
deno2/tools/lint.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
cd `dirname "$0"`/..
set -e -v
cpplint --repository=. *.cc *.h include/*.h