mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
Support scoped variables, unblock REPL async op, and REPL error colors (#1721)
This commit is contained in:
parent
1502051453
commit
1d36eb47eb
18 changed files with 329 additions and 54 deletions
1
BUILD.gn
1
BUILD.gn
|
@ -68,6 +68,7 @@ ts_sources = [
|
|||
"js/event.ts",
|
||||
"js/event_target.ts",
|
||||
"js/fetch.ts",
|
||||
"js/format_error.ts",
|
||||
"js/dom_file.ts",
|
||||
"js/file_info.ts",
|
||||
"js/files.ts",
|
||||
|
|
|
@ -3,6 +3,8 @@ import { isTypedArray } from "./util";
|
|||
import { TextEncoder } from "./text_encoding";
|
||||
import { File, stdout } from "./files";
|
||||
import { cliTable } from "./console_table";
|
||||
import { formatError } from "./format_error";
|
||||
import { libdeno } from "./libdeno";
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
type ConsoleContext = Set<any>;
|
||||
|
@ -263,7 +265,8 @@ function createObjectString(
|
|||
...args: [ConsoleContext, number, number]
|
||||
): string {
|
||||
if (value instanceof Error) {
|
||||
return value.stack! || "";
|
||||
const errorJSON = libdeno.errorToJSON(value);
|
||||
return formatError(errorJSON);
|
||||
} else if (Array.isArray(value)) {
|
||||
return createArrayString(value, ...args);
|
||||
} else if (value instanceof Number) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import { Console, libdeno, stringifyArgs, inspect, write, stdout } from "deno";
|
||||
import { test, assertEqual } from "./test_util.ts";
|
||||
import { test, assertEqual, assert } from "./test_util.ts";
|
||||
|
||||
const console = new Console(libdeno.print);
|
||||
|
||||
|
@ -245,7 +245,8 @@ test(function consoleTestError() {
|
|||
try {
|
||||
throw new MyError("This is an error");
|
||||
} catch (e) {
|
||||
assertEqual(stringify(e).split("\n")[0], "MyError: This is an error");
|
||||
assert(stringify(e).split("\n")[3]
|
||||
.includes("MyError: This is an error"));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
21
js/format_error.ts
Normal file
21
js/format_error.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import * as msg from "gen/msg_generated";
|
||||
import * as flatbuffers from "./flatbuffers";
|
||||
import { sendSync } from "./dispatch";
|
||||
import { assert } from "./util";
|
||||
|
||||
export function formatError(errString: string): string {
|
||||
const builder = flatbuffers.createBuilder();
|
||||
const errString_ = builder.createString(errString);
|
||||
msg.FormatError.startFormatError(builder);
|
||||
msg.FormatError.addError(builder, errString_);
|
||||
const offset = msg.FormatError.endFormatError(builder);
|
||||
const baseRes = sendSync(builder, msg.Any.FormatError, offset);
|
||||
assert(baseRes != null);
|
||||
assert(msg.Any.FormatErrorRes === baseRes!.innerType());
|
||||
const formatErrorResMsg = new msg.FormatErrorRes();
|
||||
assert(baseRes!.inner(formatErrorResMsg) != null);
|
||||
const formattedError = formatErrorResMsg.error();
|
||||
assert(formatError != null);
|
||||
return formattedError!;
|
||||
}
|
|
@ -3,11 +3,17 @@ import { globalEval } from "./global_eval";
|
|||
|
||||
// The libdeno functions are moved so that users can't access them.
|
||||
type MessageCallback = (msg: Uint8Array) => void;
|
||||
export type PromiseRejectEvent =
|
||||
| "RejectWithNoHandler"
|
||||
| "HandlerAddedAfterReject"
|
||||
| "ResolveAfterResolved"
|
||||
| "RejectAfterResolved";
|
||||
|
||||
interface EvalErrorInfo {
|
||||
// Is the object thrown a native Error?
|
||||
isNativeError: boolean;
|
||||
// Was the error happened during compilation?
|
||||
isCompileError: boolean;
|
||||
// The actual thrown entity
|
||||
// (might be an Error or anything else thrown by the user)
|
||||
// If isNativeError is true, this is an Error
|
||||
thrown: any; // tslint:disable-line:no-any
|
||||
}
|
||||
|
||||
interface Libdeno {
|
||||
recv(cb: MessageCallback): void;
|
||||
|
@ -20,26 +26,17 @@ interface Libdeno {
|
|||
|
||||
builtinModules: { [s: string]: object };
|
||||
|
||||
setGlobalErrorHandler: (
|
||||
handler: (
|
||||
message: string,
|
||||
source: string,
|
||||
line: number,
|
||||
col: number,
|
||||
error: Error
|
||||
) => void
|
||||
) => void;
|
||||
/** Evaluate provided code in the current context.
|
||||
* It differs from eval(...) in that it does not create a new context.
|
||||
* Returns an array: [output, errInfo].
|
||||
* If an error occurs, `output` becomes null and `errInfo` is non-null.
|
||||
*/
|
||||
evalContext(
|
||||
code: string
|
||||
): [any, EvalErrorInfo | null] /* tslint:disable-line:no-any */;
|
||||
|
||||
setPromiseRejectHandler: (
|
||||
handler: (
|
||||
error: Error | string,
|
||||
event: PromiseRejectEvent,
|
||||
/* tslint:disable-next-line:no-any */
|
||||
promise: Promise<any>
|
||||
) => void
|
||||
) => void;
|
||||
|
||||
setPromiseErrorExaminer: (handler: () => boolean) => void;
|
||||
// tslint:disable-next-line:no-any
|
||||
errorToJSON: (e: Error) => string;
|
||||
}
|
||||
|
||||
const window = globalEval("this");
|
||||
|
|
19
js/repl.ts
19
js/repl.ts
|
@ -7,6 +7,8 @@ import { close } from "./files";
|
|||
import * as dispatch from "./dispatch";
|
||||
import { exit } from "./os";
|
||||
import { globalEval } from "./global_eval";
|
||||
import { libdeno } from "./libdeno";
|
||||
import { formatError } from "./format_error";
|
||||
|
||||
const window = globalEval("this");
|
||||
|
||||
|
@ -96,14 +98,19 @@ export async function replLoop(): Promise<void> {
|
|||
}
|
||||
|
||||
function evaluate(code: string): void {
|
||||
try {
|
||||
const result = eval.call(window, code); // FIXME use a new scope.
|
||||
if (code.trim() === "") {
|
||||
return;
|
||||
}
|
||||
const [result, errInfo] = libdeno.evalContext(code);
|
||||
if (!errInfo) {
|
||||
console.log(result);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
console.error(`${err.constructor.name}: ${err.message}`);
|
||||
} else {
|
||||
if (errInfo.isNativeError) {
|
||||
const formattedError = formatError(
|
||||
libdeno.errorToJSON(errInfo.thrown as Error));
|
||||
console.error(formattedError);
|
||||
} else {
|
||||
console.error("Thrown:", err);
|
||||
console.error("Thrown:", errInfo.thrown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "third_party/v8/src/base/logging.h"
|
||||
|
||||
#include "deno.h"
|
||||
#include "exceptions.h"
|
||||
#include "internal.h"
|
||||
|
||||
extern "C" {
|
||||
|
|
|
@ -120,6 +120,16 @@ void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||
fflush(file);
|
||||
}
|
||||
|
||||
void ErrorToJSON(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
auto* isolate = args.GetIsolate();
|
||||
DenoIsolate* d = DenoIsolate::FromIsolate(isolate);
|
||||
auto context = d->context_.Get(d->isolate_);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
auto json_string = EncodeExceptionAsJSON(context, args[0]);
|
||||
args.GetReturnValue().Set(v8_str(json_string.c_str()));
|
||||
}
|
||||
|
||||
v8::Local<v8::Uint8Array> ImportBuf(DenoIsolate* d, deno_buf buf) {
|
||||
if (buf.alloc_ptr == nullptr) {
|
||||
// If alloc_ptr isn't set, we memcpy.
|
||||
|
@ -368,6 +378,80 @@ bool Execute(v8::Local<v8::Context> context, const char* js_filename,
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline v8::Local<v8::Boolean> v8_bool(bool v) {
|
||||
return v8::Boolean::New(v8::Isolate::GetCurrent(), v);
|
||||
}
|
||||
|
||||
void EvalContext(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
DenoIsolate* d = DenoIsolate::FromIsolate(isolate);
|
||||
v8::EscapableHandleScope handleScope(isolate);
|
||||
auto context = d->context_.Get(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
CHECK(args[0]->IsString());
|
||||
auto source = args[0].As<v8::String>();
|
||||
|
||||
auto output = v8::Array::New(isolate, 2);
|
||||
/**
|
||||
* output[0] = result
|
||||
* output[1] = ErrorInfo | null
|
||||
* ErrorInfo = {
|
||||
* thrown: Error | any,
|
||||
* isNativeError: boolean,
|
||||
* isCompileError: boolean,
|
||||
* }
|
||||
*/
|
||||
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
auto name = v8_str("<unknown>");
|
||||
v8::ScriptOrigin origin(name);
|
||||
auto script = v8::Script::Compile(context, source, &origin);
|
||||
|
||||
if (script.IsEmpty()) {
|
||||
DCHECK(try_catch.HasCaught());
|
||||
auto exception = try_catch.Exception();
|
||||
|
||||
output->Set(0, v8::Null(isolate));
|
||||
|
||||
auto errinfo_obj = v8::Object::New(isolate);
|
||||
errinfo_obj->Set(v8_str("isCompileError"), v8_bool(true));
|
||||
errinfo_obj->Set(v8_str("isNativeError"),
|
||||
v8_bool(exception->IsNativeError()));
|
||||
errinfo_obj->Set(v8_str("thrown"), exception);
|
||||
|
||||
output->Set(1, errinfo_obj);
|
||||
|
||||
args.GetReturnValue().Set(output);
|
||||
return;
|
||||
}
|
||||
|
||||
auto result = script.ToLocalChecked()->Run(context);
|
||||
|
||||
if (result.IsEmpty()) {
|
||||
DCHECK(try_catch.HasCaught());
|
||||
auto exception = try_catch.Exception();
|
||||
|
||||
output->Set(0, v8::Null(isolate));
|
||||
|
||||
auto errinfo_obj = v8::Object::New(isolate);
|
||||
errinfo_obj->Set(v8_str("isCompileError"), v8_bool(false));
|
||||
errinfo_obj->Set(v8_str("isNativeError"),
|
||||
v8_bool(exception->IsNativeError()));
|
||||
errinfo_obj->Set(v8_str("thrown"), exception);
|
||||
|
||||
output->Set(1, errinfo_obj);
|
||||
|
||||
args.GetReturnValue().Set(output);
|
||||
return;
|
||||
}
|
||||
|
||||
output->Set(0, result.ToLocalChecked());
|
||||
output->Set(1, v8::Null(isolate));
|
||||
args.GetReturnValue().Set(output);
|
||||
}
|
||||
|
||||
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) {
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
@ -389,6 +473,18 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) {
|
|||
auto send_val = send_tmpl->GetFunction(context).ToLocalChecked();
|
||||
CHECK(deno_val->Set(context, deno::v8_str("send"), send_val).FromJust());
|
||||
|
||||
auto eval_context_tmpl = v8::FunctionTemplate::New(isolate, EvalContext);
|
||||
auto eval_context_val =
|
||||
eval_context_tmpl->GetFunction(context).ToLocalChecked();
|
||||
CHECK(deno_val->Set(context, deno::v8_str("evalContext"), eval_context_val)
|
||||
.FromJust());
|
||||
|
||||
auto error_to_json_tmpl = v8::FunctionTemplate::New(isolate, ErrorToJSON);
|
||||
auto error_to_json_val =
|
||||
error_to_json_tmpl->GetFunction(context).ToLocalChecked();
|
||||
CHECK(deno_val->Set(context, deno::v8_str("errorToJSON"), error_to_json_val)
|
||||
.FromJust());
|
||||
|
||||
CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared)
|
||||
.FromJust());
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
|
||||
namespace deno {
|
||||
|
||||
std::string EncodeMessageAsJSON(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Message> message) {
|
||||
v8::Local<v8::Object> EncodeMessageAsObject(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Message> message) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::EscapableHandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
auto stack_trace = message->GetStackTrace();
|
||||
|
@ -134,12 +134,33 @@ std::string EncodeMessageAsJSON(v8::Local<v8::Context> context,
|
|||
}
|
||||
|
||||
CHECK(json_obj->Set(context, v8_str("frames"), frames).FromJust());
|
||||
json_obj = handle_scope.Escape(json_obj);
|
||||
return json_obj;
|
||||
}
|
||||
|
||||
std::string EncodeMessageAsJSON(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Message> message) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
auto json_obj = EncodeMessageAsObject(context, message);
|
||||
auto json_string = v8::JSON::Stringify(context, json_obj).ToLocalChecked();
|
||||
v8::String::Utf8Value json_string_(isolate, json_string);
|
||||
return std::string(ToCString(json_string_));
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> EncodeExceptionAsObject(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
v8::EscapableHandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
auto message = v8::Exception::CreateMessage(isolate, exception);
|
||||
auto json_obj = EncodeMessageAsObject(context, message);
|
||||
json_obj = handle_scope.Escape(json_obj);
|
||||
return json_obj;
|
||||
}
|
||||
|
||||
std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
@ -167,5 +188,4 @@ void HandleExceptionMessage(v8::Local<v8::Context> context,
|
|||
CHECK_NOT_NULL(d);
|
||||
d->last_exception_ = json_str;
|
||||
}
|
||||
|
||||
} // namespace deno
|
||||
|
|
|
@ -2,10 +2,17 @@
|
|||
#ifndef EXCEPTIONS_H_
|
||||
#define EXCEPTIONS_H_
|
||||
|
||||
#include <string>
|
||||
#include "third_party/v8/include/v8.h"
|
||||
|
||||
namespace deno {
|
||||
|
||||
v8::Local<v8::Object> EncodeExceptionAsObject(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception);
|
||||
|
||||
std::string EncodeExceptionAsJSON(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception);
|
||||
|
||||
void HandleException(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception);
|
||||
|
||||
|
|
|
@ -133,6 +133,8 @@ static inline v8::Local<v8::String> v8_str(const char* x) {
|
|||
void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void EvalContext(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void ErrorToJSON(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
void Shared(v8::Local<v8::Name> property,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
void BuiltinModules(v8::Local<v8::Name> property,
|
||||
|
@ -142,6 +144,8 @@ static intptr_t external_references[] = {
|
|||
reinterpret_cast<intptr_t>(Print),
|
||||
reinterpret_cast<intptr_t>(Recv),
|
||||
reinterpret_cast<intptr_t>(Send),
|
||||
reinterpret_cast<intptr_t>(EvalContext),
|
||||
reinterpret_cast<intptr_t>(ErrorToJSON),
|
||||
reinterpret_cast<intptr_t>(Shared),
|
||||
reinterpret_cast<intptr_t>(BuiltinModules),
|
||||
reinterpret_cast<intptr_t>(MessageCallback),
|
||||
|
@ -153,9 +157,6 @@ Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
|
|||
|
||||
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context);
|
||||
|
||||
void HandleException(v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exception);
|
||||
|
||||
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
|
||||
v8::StartupData payload, void* data);
|
||||
|
||||
|
|
|
@ -290,6 +290,20 @@ TEST(LibDenoTest, Utf8Bug) {
|
|||
deno_delete(d);
|
||||
}
|
||||
|
||||
TEST(LibDenoTest, LibDenoEvalContext) {
|
||||
Deno* d = deno_new(deno_config{0, snapshot, empty, nullptr});
|
||||
deno_execute(d, nullptr, "a.js", "LibDenoEvalContext();");
|
||||
EXPECT_EQ(nullptr, deno_last_exception(d));
|
||||
deno_delete(d);
|
||||
}
|
||||
|
||||
TEST(LibDenoTest, LibDenoEvalContextError) {
|
||||
Deno* d = deno_new(deno_config{0, snapshot, empty, nullptr});
|
||||
deno_execute(d, nullptr, "a.js", "LibDenoEvalContextError();");
|
||||
EXPECT_EQ(nullptr, deno_last_exception(d));
|
||||
deno_delete(d);
|
||||
}
|
||||
|
||||
TEST(LibDenoTest, SharedAtomics) {
|
||||
int32_t s[] = {0, 1, 2};
|
||||
deno_buf shared = {nullptr, 0, reinterpret_cast<uint8_t*>(s), sizeof s};
|
||||
|
|
|
@ -147,3 +147,50 @@ global.Shared = () => {
|
|||
ui8[1] = 43;
|
||||
ui8[2] = 44;
|
||||
};
|
||||
|
||||
global.LibDenoEvalContext = () => {
|
||||
const [result, errInfo] = libdeno.evalContext("let a = 1; a");
|
||||
assert(result === 1);
|
||||
assert(!errInfo);
|
||||
const [result2, errInfo2] = libdeno.evalContext("a = a + 1; a");
|
||||
assert(result2 === 2);
|
||||
assert(!errInfo2);
|
||||
};
|
||||
|
||||
global.LibDenoEvalContextError = () => {
|
||||
const [result, errInfo] = libdeno.evalContext("not_a_variable");
|
||||
assert(!result);
|
||||
assert(!!errInfo);
|
||||
assert(errInfo.isNativeError); // is a native error (ReferenceError)
|
||||
assert(!errInfo.isCompileError); // is NOT a compilation error
|
||||
assert(errInfo.thrown.message === "not_a_variable is not defined");
|
||||
|
||||
const [result2, errInfo2] = libdeno.evalContext("throw 1");
|
||||
assert(!result2);
|
||||
assert(!!errInfo2);
|
||||
assert(!errInfo2.isNativeError); // is NOT a native error
|
||||
assert(!errInfo2.isCompileError); // is NOT a compilation error
|
||||
assert(errInfo2.thrown === 1);
|
||||
|
||||
const [result3, errInfo3] =
|
||||
libdeno.evalContext("class AError extends Error {}; throw new AError('e')");
|
||||
assert(!result3);
|
||||
assert(!!errInfo3);
|
||||
assert(errInfo3.isNativeError); // extend from native error, still native error
|
||||
assert(!errInfo3.isCompileError); // is NOT a compilation error
|
||||
assert(errInfo3.thrown.message === "e");
|
||||
|
||||
const [result4, errInfo4] = libdeno.evalContext("{");
|
||||
assert(!result4);
|
||||
assert(!!errInfo4);
|
||||
assert(errInfo4.isNativeError); // is a native error (SyntaxError)
|
||||
assert(errInfo4.isCompileError); // is a compilation error! (braces not closed)
|
||||
assert(errInfo4.thrown.message === "Unexpected end of input");
|
||||
|
||||
const [result5, errInfo5] = libdeno.evalContext("eval('{')");
|
||||
assert(!result5);
|
||||
assert(!!errInfo5);
|
||||
assert(errInfo5.isNativeError); // is a native error (SyntaxError)
|
||||
assert(!errInfo5.isCompileError); // is NOT a compilation error! (just eval)
|
||||
assert(errInfo5.thrown.message === "Unexpected end of input");
|
||||
};
|
||||
|
|
|
@ -178,5 +178,4 @@ extern "C" {
|
|||
user_data: *const c_void,
|
||||
id: deno_mod,
|
||||
);
|
||||
|
||||
}
|
||||
|
|
10
src/msg.fbs
10
src/msg.fbs
|
@ -1,6 +1,8 @@
|
|||
union Any {
|
||||
Start,
|
||||
StartRes,
|
||||
FormatError,
|
||||
FormatErrorRes,
|
||||
WorkerGetMessage,
|
||||
WorkerGetMessageRes,
|
||||
WorkerPostMessage,
|
||||
|
@ -163,6 +165,14 @@ table StartRes {
|
|||
no_color: bool;
|
||||
}
|
||||
|
||||
table FormatError {
|
||||
error: string;
|
||||
}
|
||||
|
||||
table FormatErrorRes {
|
||||
error: string;
|
||||
}
|
||||
|
||||
table WorkerGetMessage {
|
||||
unused: int8;
|
||||
}
|
||||
|
|
40
src/ops.rs
40
src/ops.rs
|
@ -10,6 +10,7 @@ use crate::isolate::Buf;
|
|||
use crate::isolate::Isolate;
|
||||
use crate::isolate::IsolateState;
|
||||
use crate::isolate::Op;
|
||||
use crate::js_errors::JSError;
|
||||
use crate::libdeno;
|
||||
use crate::msg;
|
||||
use crate::msg_util;
|
||||
|
@ -97,6 +98,7 @@ pub fn dispatch(
|
|||
msg::Any::Environ => op_env,
|
||||
msg::Any::Exit => op_exit,
|
||||
msg::Any::Fetch => op_fetch,
|
||||
msg::Any::FormatError => op_format_error,
|
||||
msg::Any::Listen => op_listen,
|
||||
msg::Any::MakeTempDir => op_make_temp_dir,
|
||||
msg::Any::Metrics => op_metrics,
|
||||
|
@ -283,6 +285,41 @@ fn op_start(
|
|||
))
|
||||
}
|
||||
|
||||
fn op_format_error(
|
||||
state: &Arc<IsolateState>,
|
||||
base: &msg::Base<'_>,
|
||||
data: libdeno::deno_buf,
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
let inner = base.inner_as_format_error().unwrap();
|
||||
let orig_error = String::from(inner.error().unwrap());
|
||||
|
||||
let js_error = JSError::from_v8_exception(&orig_error).unwrap();
|
||||
let js_error_mapped = js_error.apply_source_map(&state.dir);
|
||||
let js_error_string = js_error_mapped.to_string();
|
||||
|
||||
let mut builder = FlatBufferBuilder::new();
|
||||
let new_error = builder.create_string(&js_error_string);
|
||||
|
||||
let inner = msg::FormatErrorRes::create(
|
||||
&mut builder,
|
||||
&msg::FormatErrorResArgs {
|
||||
error: Some(new_error),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
ok_future(serialize_response(
|
||||
base.cmd_id(),
|
||||
&mut builder,
|
||||
msg::BaseArgs {
|
||||
inner_type: msg::Any::FormatErrorRes,
|
||||
inner: Some(inner.as_union_value()),
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn serialize_response(
|
||||
cmd_id: u32,
|
||||
builder: &mut FlatBufferBuilder<'_>,
|
||||
|
@ -1271,7 +1308,8 @@ fn op_repl_readline(
|
|||
debug!("op_repl_readline {} {}", rid, prompt);
|
||||
|
||||
blocking(base.sync(), move || -> OpResult {
|
||||
let line = resources::readline(rid, &prompt)?;
|
||||
let repl = resources::get_repl(rid)?;
|
||||
let line = repl.lock().unwrap().readline(&prompt)?;
|
||||
|
||||
let builder = &mut FlatBufferBuilder::new();
|
||||
let line_off = builder.create_string(&line);
|
||||
|
|
|
@ -35,7 +35,7 @@ use std::net::{Shutdown, SocketAddr};
|
|||
use std::process::ExitStatus;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -95,7 +95,7 @@ enum Repr {
|
|||
TcpListener(tokio::net::TcpListener, Option<futures::task::Task>),
|
||||
TcpStream(tokio::net::TcpStream),
|
||||
HttpBody(HttpBody),
|
||||
Repl(Repl),
|
||||
Repl(Arc<Mutex<Repl>>),
|
||||
// Enum size is bounded by the largest variant.
|
||||
// Use `Box` around large `Child` struct.
|
||||
// https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
|
||||
|
@ -334,7 +334,7 @@ pub fn add_hyper_body(body: hyper::Body) -> Resource {
|
|||
pub fn add_repl(repl: Repl) -> Resource {
|
||||
let rid = new_rid();
|
||||
let mut tg = RESOURCE_TABLE.lock().unwrap();
|
||||
let r = tg.insert(rid, Repr::Repl(repl));
|
||||
let r = tg.insert(rid, Repr::Repl(Arc::new(Mutex::new(repl))));
|
||||
assert!(r.is_none());
|
||||
Resource { rid }
|
||||
}
|
||||
|
@ -462,14 +462,11 @@ pub fn child_status(rid: ResourceId) -> DenoResult<ChildStatus> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn readline(rid: ResourceId, prompt: &str) -> DenoResult<String> {
|
||||
pub fn get_repl(rid: ResourceId) -> DenoResult<Arc<Mutex<Repl>>> {
|
||||
let mut table = RESOURCE_TABLE.lock().unwrap();
|
||||
let maybe_repr = table.get_mut(&rid);
|
||||
match maybe_repr {
|
||||
Some(Repr::Repl(ref mut r)) => {
|
||||
let line = r.readline(&prompt)?;
|
||||
Ok(line)
|
||||
}
|
||||
Some(Repr::Repl(ref mut r)) => Ok(r.clone()),
|
||||
_ => Err(bad_resource()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class Repl(object):
|
|||
def input(self, *lines, **kwargs):
|
||||
exit_ = kwargs.pop("exit", True)
|
||||
sleep_ = kwargs.pop("sleep", 0)
|
||||
p = Popen([self.deno_exe], stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
p = Popen([self.deno_exe, "-A"], stdout=PIPE, stderr=PIPE, stdin=PIPE)
|
||||
try:
|
||||
# Note: The repl takes a >100ms until it's ready.
|
||||
time.sleep(sleep_)
|
||||
|
@ -87,7 +87,7 @@ class Repl(object):
|
|||
def test_reference_error(self):
|
||||
out, err, code = self.input("not_a_variable")
|
||||
assertEqual(out, '')
|
||||
assertEqual(err, 'ReferenceError: not_a_variable is not defined\n')
|
||||
assert "not_a_variable is not defined" in err
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_set_timeout(self):
|
||||
|
@ -108,16 +108,25 @@ class Repl(object):
|
|||
assertEqual(err, '')
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_async_op(self):
|
||||
out, err, code = self.input(
|
||||
"fetch('http://localhost:4545/tests/001_hello.js')" +
|
||||
".then(res => res.text()).then(console.log)",
|
||||
sleep=1)
|
||||
assertEqual(out, 'Promise {}\nconsole.log("Hello World");\n\n')
|
||||
assertEqual(err, '')
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_syntax_error(self):
|
||||
out, err, code = self.input("syntax error")
|
||||
assertEqual(out, '')
|
||||
assertEqual(err, "SyntaxError: Unexpected identifier\n")
|
||||
assert "Unexpected identifier" in err
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_type_error(self):
|
||||
out, err, code = self.input("console()")
|
||||
assertEqual(out, '')
|
||||
assertEqual(err, 'TypeError: console is not a function\n')
|
||||
assert "console is not a function" in err
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_variable(self):
|
||||
|
@ -126,6 +135,12 @@ class Repl(object):
|
|||
assertEqual(err, '')
|
||||
assertEqual(code, 0)
|
||||
|
||||
def test_lexical_scoped_variable(self):
|
||||
out, err, code = self.input("let a = 123;", "a")
|
||||
assertEqual(out, 'undefined\n123\n')
|
||||
assertEqual(err, '')
|
||||
assertEqual(code, 0)
|
||||
|
||||
|
||||
def assertEqual(left, right):
|
||||
if left != right:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue