summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/js/globals.ts13
-rw-r--r--cli/js/tests/console_test.ts21
-rw-r--r--cli/js/web/console.ts34
-rw-r--r--cli/js/web/promise.ts7
-rw-r--r--core/bindings.rs79
5 files changed, 153 insertions, 1 deletions
diff --git a/cli/js/globals.ts b/cli/js/globals.ts
index 87ff13b22..e5b7ff85a 100644
--- a/cli/js/globals.ts
+++ b/cli/js/globals.ts
@@ -2,6 +2,7 @@
import * as blob from "./web/blob.ts";
import * as consoleTypes from "./web/console.ts";
+import * as promiseTypes from "./web/promise.ts";
import * as customEvent from "./web/custom_event.ts";
import * as domTypes from "./web/dom_types.ts";
import * as domFile from "./web/dom_file.ts";
@@ -102,6 +103,18 @@ declare global {
formatError: (e: Error) => string;
+ /**
+ * Get promise details as two elements array.
+ *
+ * First element is the `PromiseState`.
+ * If promise isn't pending, second element would be the result of the promise.
+ * Otherwise, second element would be undefined.
+ *
+ * Throws `TypeError` if argument isn't a promise
+ *
+ */
+ getPromiseDetails<T>(promise: Promise<T>): promiseTypes.PromiseDetails<T>;
+
decode(bytes: Uint8Array): string;
encode(text: string): Uint8Array;
}
diff --git a/cli/js/tests/console_test.ts b/cli/js/tests/console_test.ts
index c49c941f4..88902f84b 100644
--- a/cli/js/tests/console_test.ts
+++ b/cli/js/tests/console_test.ts
@@ -580,6 +580,27 @@ unitTest(function consoleTestStringifyIterable() {
);
});
+unitTest(async function consoleTestStringifyPromises(): Promise<void> {
+ const pendingPromise = new Promise((_res, _rej) => {});
+ assertEquals(stringify(pendingPromise), "Promise { <pending> }");
+
+ const resolvedPromise = new Promise((res, _rej) => {
+ res("Resolved!");
+ });
+ assertEquals(stringify(resolvedPromise), `Promise { "Resolved!" }`);
+
+ let rejectedPromise;
+ try {
+ rejectedPromise = new Promise((_, rej) => {
+ rej(Error("Whoops"));
+ });
+ await rejectedPromise;
+ } catch (err) {}
+ const strLines = stringify(rejectedPromise).split("\n");
+ assertEquals(strLines[0], "Promise {");
+ assertEquals(strLines[1], " <rejected> Error: Whoops");
+});
+
unitTest(function consoleTestWithCustomInspector(): void {
class A {
[customInspect](): string {
diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts
index a9b4d53be..3a274e086 100644
--- a/cli/js/web/console.ts
+++ b/cli/js/web/console.ts
@@ -4,6 +4,7 @@ import { TextEncoder } from "./text_encoding.ts";
import { File, stdout } from "../files.ts";
import { cliTable } from "./console_table.ts";
import { exposeForTest } from "../internals.ts";
+import { PromiseState } from "./promise.ts";
type ConsoleContext = Set<unknown>;
type InspectOptions = Partial<{
@@ -28,6 +29,8 @@ const CHAR_LOWERCASE_O = 111; /* o */
const CHAR_UPPERCASE_O = 79; /* O */
const CHAR_LOWERCASE_C = 99; /* c */
+const PROMISE_STRING_BASE_LENGTH = 12;
+
export class CSI {
static kClear = "\x1b[1;1H";
static kClearScreenDown = "\x1b[0J";
@@ -442,7 +445,34 @@ function createNumberWrapperString(value: Number): string {
/* eslint-enable @typescript-eslint/ban-types */
-// TODO: Promise, requires v8 bindings to get info
+function createPromiseString(
+ value: Promise<unknown>,
+ ctx: ConsoleContext,
+ level: number,
+ maxLevel: number
+): string {
+ const [state, result] = Deno.core.getPromiseDetails(value);
+
+ if (state === PromiseState.Pending) {
+ return "Promise { <pending> }";
+ }
+
+ const prefix = state === PromiseState.Fulfilled ? "" : "<rejected> ";
+
+ const str = `${prefix}${stringifyWithQuotes(
+ result,
+ ctx,
+ level + 1,
+ maxLevel
+ )}`;
+
+ if (str.length + PROMISE_STRING_BASE_LENGTH > LINE_BREAKING_LENGTH) {
+ return `Promise {\n${" ".repeat(level + 1)}${str}\n}`;
+ }
+
+ return `Promise { ${str} }`;
+}
+
// TODO: Proxy
function createRawObjectString(
@@ -531,6 +561,8 @@ function createObjectString(
return createBooleanWrapperString(value);
} else if (value instanceof String) {
return createStringWrapperString(value);
+ } else if (value instanceof Promise) {
+ return createPromiseString(value, ...args);
} else if (value instanceof RegExp) {
return createRegExpString(value);
} else if (value instanceof Date) {
diff --git a/cli/js/web/promise.ts b/cli/js/web/promise.ts
new file mode 100644
index 000000000..b00c0786f
--- /dev/null
+++ b/cli/js/web/promise.ts
@@ -0,0 +1,7 @@
+export enum PromiseState {
+ Pending = 0,
+ Fulfilled = 1,
+ Rejected = 2,
+}
+
+export type PromiseDetails<T> = [PromiseState, T | undefined];
diff --git a/core/bindings.rs b/core/bindings.rs
index 88bdf7f30..c89c68424 100644
--- a/core/bindings.rs
+++ b/core/bindings.rs
@@ -45,6 +45,9 @@ lazy_static! {
v8::ExternalReference {
function: decode.map_fn_to()
},
+ v8::ExternalReference {
+ function: get_promise_details.map_fn_to(),
+ }
]);
}
@@ -195,6 +198,17 @@ pub fn initialize_context<'s>(
decode_val.into(),
);
+ let mut get_promise_details_tmpl =
+ v8::FunctionTemplate::new(scope, get_promise_details);
+ let get_promise_details_val = get_promise_details_tmpl
+ .get_function(scope, context)
+ .unwrap();
+ core_val.set(
+ context,
+ v8::String::new(scope, "getPromiseDetails").unwrap().into(),
+ get_promise_details_val.into(),
+ );
+
core_val.set_accessor(
context,
v8::String::new(scope, "shared").unwrap().into(),
@@ -761,3 +775,68 @@ pub fn module_resolve_callback<'s>(
None
}
+
+// Returns promise details or throw TypeError, if argument passed isn't a Promise.
+// Promise details is a two elements array.
+// promise_details = [State, Result]
+// State = enum { Pending = 0, Fulfilled = 1, Rejected = 2}
+// Result = PromiseResult<T> | PromiseError
+fn get_promise_details(
+ scope: v8::FunctionCallbackScope,
+ args: v8::FunctionCallbackArguments,
+ mut rv: v8::ReturnValue,
+) {
+ let deno_isolate: &mut Isolate =
+ unsafe { &mut *(scope.isolate().get_data(0) as *mut Isolate) };
+ assert!(!deno_isolate.global_context.is_empty());
+ let context = deno_isolate.global_context.get(scope).unwrap();
+
+ let mut promise = match v8::Local::<v8::Promise>::try_from(args.get(0)) {
+ Ok(val) => val,
+ Err(_) => {
+ let msg = v8::String::new(scope, "Invalid argument").unwrap();
+ let exception = v8::Exception::type_error(scope, msg);
+ scope.isolate().throw_exception(exception);
+ return;
+ }
+ };
+
+ let promise_details = v8::Array::new(scope, 2);
+
+ match promise.state() {
+ v8::PromiseState::Pending => {
+ promise_details.set(
+ context,
+ v8::Integer::new(scope, 0).into(),
+ v8::Integer::new(scope, 0).into(),
+ );
+ rv.set(promise_details.into());
+ }
+ v8::PromiseState::Fulfilled => {
+ promise_details.set(
+ context,
+ v8::Integer::new(scope, 0).into(),
+ v8::Integer::new(scope, 1).into(),
+ );
+ promise_details.set(
+ context,
+ v8::Integer::new(scope, 1).into(),
+ promise.result(scope),
+ );
+ rv.set(promise_details.into());
+ }
+ v8::PromiseState::Rejected => {
+ promise_details.set(
+ context,
+ v8::Integer::new(scope, 0).into(),
+ v8::Integer::new(scope, 2).into(),
+ );
+ promise_details.set(
+ context,
+ v8::Integer::new(scope, 1).into(),
+ promise.result(scope),
+ );
+ rv.set(promise_details.into());
+ }
+ }
+}