diff --git a/core/01_core.js b/core/01_core.js index 285b3bb2a..cb5368863 100644 --- a/core/01_core.js +++ b/core/01_core.js @@ -23,11 +23,17 @@ TypeError, } = window.__bootstrap.primordials; const { + coreOps, ops, getPromise, hasPromise, promiseIdSymbol, } = window.Deno.core; + // TODO(bartlomieju): because op bindings are generated on each startup + // we can't remove it right now. See https://github.com/denoland/deno_core/issues/521. + // Delete `Deno.core.coreOps` immediately. These bindings shouldn't be accessible + // outside deno_core. + delete window.Deno.core.coreOps; let unhandledPromiseRejectionHandler = () => false; let timerDepth = 0; @@ -302,7 +308,7 @@ op_read_sync: readSync, op_write_sync: writeSync, op_shutdown: shutdown, - } = ensureFastOps(true); + } = coreOps; const callSiteRetBuf = new Uint32Array(2); const callSiteRetBufU8 = new Uint8Array(callSiteRetBuf.buffer); @@ -421,10 +427,21 @@ op_set_wasm_streaming_callback, op_str_byte_length, op_timer_cancel, + op_void_async, + op_void_async_deferred, + op_set_format_exception_callback, + op_format_file_name, + op_apply_source_map_filename, + op_wasm_streaming_feed, + op_wasm_streaming_set_url, + op_apply_source_map, op_timer_queue, op_timer_ref, op_timer_unref, op_unref_op, + op_arraybuffer_was_detached, + op_get_constructor_name, + op_get_non_index_property_names, op_is_any_array_buffer, op_is_arguments_object, @@ -454,7 +471,7 @@ op_is_typed_array, op_is_weak_map, op_is_weak_set, - } = ensureFastOps(); + } = coreOps; function propWritable(value) { return { @@ -617,6 +634,8 @@ isRegExp: (value) => op_is_reg_exp(value), isSet: (value) => op_is_set(value), isSetIterator: (value) => op_is_set_iterator(value), + wasmStreamingFeed: (rid, bytes) => op_wasm_streaming_feed(rid, bytes), + wasmStreamingSetUrl: (rid, url) => op_wasm_streaming_set_url(rid, url), isSharedArrayBuffer: (value) => op_is_shared_array_buffer(value), isStringObject: (value) => op_is_string_object(value), isSymbolObject: (value) => op_is_symbol_object(value), @@ -632,7 +651,18 @@ destructureError: (error) => op_destructure_error(error), opNames: () => op_op_names(), eventLoopHasMoreWork: () => op_event_loop_has_more_work(), + setFormatExceptionCallback: (cb) => op_set_format_exception_callback(cb), + arrayBufferWasDetached: (buf) => op_arraybuffer_was_detached(buf), + getConstructorName: (obj) => op_get_constructor_name(obj), + getNonIndexPropertyNames: (obj, filter) => + op_get_non_index_property_names(obj, filter), + applySourceMapFilename: () => op_apply_source_map_filename(), + applySourceMap: (fileName, lineNumber, columnNumber, retBuf) => + op_apply_source_map(fileName, lineNumber, columnNumber, retBuf), + formatFileName: (fileName) => op_format_file_name(fileName), byteLength: (str) => op_str_byte_length(str), + opVoidAsync: () => op_void_async(), + opVoidAsyncDeferred: () => op_void_async_deferred(), setHandledPromiseRejectionHandler: (handler) => op_set_handled_promise_rejection_handler(handler), setUnhandledPromiseRejectionHandler: (handler) => diff --git a/core/02_error.js b/core/02_error.js index 4c297eae0..52c2c9c3e 100644 --- a/core/02_error.js +++ b/core/02_error.js @@ -3,7 +3,6 @@ ((window) => { const core = Deno.core; - const ops = core.ops; const { Error, ObjectDefineProperties, @@ -22,7 +21,7 @@ fileName.startsWith("data:") && fileName.length > DATA_URL_ABBREV_THRESHOLD ) { - return ops.op_format_file_name(fileName); + return core.formatFileName(fileName); } return fileName; } @@ -151,7 +150,7 @@ callSite.fileName !== null && callSite.lineNumber !== null && callSite.columnNumber !== null ) { - res = ops.op_apply_source_map( + res = core.applySourceMap( callSite.fileName, callSite.lineNumber, callSite.columnNumber, @@ -163,7 +162,7 @@ callSite.columnNumber = applySourceMapRetBuf[1]; } if (res >= 2) { - callSite.fileName = ops.op_apply_source_map_filename(); + callSite.fileName = core.applySourceMapFilename(); } ArrayPrototypePush(error.__callSiteEvals, callSite); stack += `\n at ${formatCallSiteEval(callSite)}`; diff --git a/core/runtime/bindings.rs b/core/runtime/bindings.rs index 447739947..a9140feac 100644 --- a/core/runtime/bindings.rs +++ b/core/runtime/bindings.rs @@ -113,6 +113,24 @@ pub fn script_origin<'a>( ) } +macro_rules! get_todo { + ($type:ty, $scope: expr, $from:expr, $key:expr, $path:literal) => {{ + let temp = v8_static_strings::new($scope, $key).into(); + TryInto::<$type>::try_into( + $from + .get($scope, temp) + .unwrap_or_else(|| panic!("{} exists", $path)), + ) + .unwrap_or_else(|_| { + panic!( + "Unable to convert {} to desired {}", + $path, + stringify!($type) + ) + }) + }}; +} + pub(crate) fn get<'s, T>( scope: &mut v8::HandleScope<'s>, from: v8::Local, @@ -127,7 +145,7 @@ where .get(scope, key.into()) .unwrap_or_else(|| panic!("{path} exists")) .try_into() - .unwrap_or_else(|_| panic!("unable to convert")) + .unwrap_or_else(|_| panic!("Unable to convert {path} to desired type")) } pub mod v8_static_strings { @@ -140,6 +158,7 @@ pub mod v8_static_strings { pub static DENO: &[u8] = b"Deno"; pub static CORE: &[u8] = b"core"; + pub static CORE_OPS: &[u8] = b"coreOps"; pub static OPS: &[u8] = b"ops"; pub static URL: &[u8] = b"url"; pub static MAIN: &[u8] = b"main"; @@ -200,6 +219,20 @@ pub(crate) fn initialize_deno_core_namespace<'s>( .set(scope, deno_core_ops_key.into(), deno_core_ops_obj.into()) .unwrap(); + // Set up `Deno.core.coreOps` object + let deno_core_core_ops_obj = v8::Object::new(scope); + let deno_core_core_ops_key = + v8::String::new_external_onebyte_static(scope, v8_static_strings::CORE_OPS) + .unwrap(); + + deno_core_obj + .set( + scope, + deno_core_core_ops_key.into(), + deno_core_core_ops_obj.into(), + ) + .unwrap(); + // If we're initializing fresh context set up the console if init_mode == InitMode::New { // Bind `call_console` to Deno.core.callConsole @@ -210,11 +243,12 @@ pub(crate) fn initialize_deno_core_namespace<'s>( // Bind v8 console object to Deno.core.console let extra_binding_obj = context.get_extras_binding_object(scope); - let console_obj: v8::Local = get( + let console_obj = get_todo!( + v8::Local, scope, extra_binding_obj, v8_static_strings::CONSOLE, - "ExtrasBindingObject.console", + "ExtrasBindingObject.console" ); let console_key = v8_static_strings::new(scope, v8_static_strings::CONSOLE); deno_core_obj.set(scope, console_key.into(), console_obj.into()); @@ -245,10 +279,13 @@ pub(crate) fn initialize_primordials_and_infra( } /// Set up JavaScript bindings for ops. -pub(crate) fn initialize_deno_core_ops_bindings<'s>( +pub(crate) fn initialize_ops_bindings<'s>( scope: &mut v8::HandleScope<'s>, context: v8::Local<'s, v8::Context>, op_ctxs: &[OpCtx], + init_mode: InitMode, + // TODO(bartlomieju): this is really hacky solution + last_deno_core_op_id: usize, ) { let global = context.global(scope); @@ -264,6 +301,7 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( v8_static_strings::OPS, "Deno.core.ops", ); + let set_up_async_stub_fn: v8::Local = get( scope, deno_core_obj, @@ -272,7 +310,41 @@ pub(crate) fn initialize_deno_core_ops_bindings<'s>( ); let undefined = v8::undefined(scope); - for op_ctx in op_ctxs { + + let deno_core_op_ctxs = &op_ctxs[..last_deno_core_op_id]; + let ext_op_ctxs = &op_ctxs[last_deno_core_op_id..]; + + // TODO(bartlomieju): hoist this to a separate function + if init_mode == InitMode::New { + let deno_core_core_ops_obj: v8::Local = get( + scope, + deno_core_obj, + v8_static_strings::CORE_OPS, + "Deno.core.coreOps", + ); + for op_ctx in deno_core_op_ctxs { + let mut op_fn = op_ctx_function(scope, op_ctx); + let key = v8::String::new_external_onebyte_static( + scope, + op_ctx.decl.name.as_bytes(), + ) + .unwrap(); + + // For async ops we need to set them up, by calling `Deno.core.setUpAsyncStub` - + // this call will generate an optimized function that binds to the provided + // op, while keeping track of promises and error remapping. + if op_ctx.decl.is_async { + let result = set_up_async_stub_fn + .call(scope, undefined.into(), &[key.into(), op_fn.into()]) + .unwrap(); + op_fn = result.try_into().unwrap() + } + + deno_core_core_ops_obj.set(scope, key.into(), op_fn.into()); + } + } + + for op_ctx in ext_op_ctxs { let mut op_fn = op_ctx_function(scope, op_ctx); let key = v8_static_strings::new(scope, op_ctx.decl.name.as_bytes()); diff --git a/core/runtime/jsruntime.rs b/core/runtime/jsruntime.rs index 5614971e0..baba03cd3 100644 --- a/core/runtime/jsruntime.rs +++ b/core/runtime/jsruntime.rs @@ -264,7 +264,7 @@ impl Future for RcPromiseFuture { } } -const VIRTUAL_OPS_MODULE_NAME: &str = "ext:core/ops"; +pub(crate) const VIRTUAL_OPS_MODULE_NAME: &str = "ext:core/ops"; /// These files are executed just after a new context is created. They provided /// the necessary infrastructure to bind ops. @@ -728,10 +728,15 @@ impl JsRuntime { // If we're creating a new runtime or there are new ops to register // set up JavaScript bindings for them. if init_mode.needs_ops_bindings() { - bindings::initialize_deno_core_ops_bindings( + // TODO(bartlomieju): this is a really hacky solution and relies + // implicitly on how `extension_set::create_op_ctxs` works. + let last_deno_core_op_id = crate::ops_builtin::BUILTIN_OPS.len() - 1; + bindings::initialize_ops_bindings( scope, context, &context_state.op_ctxs, + init_mode, + last_deno_core_op_id, ); } @@ -942,8 +947,13 @@ impl JsRuntime { let context_local = v8::Local::new(scope, context_global); let context_state = JsRealm::state_from_scope(scope); let global = context_local.global(scope); + + // TODO(bartlomieju): this is a really hacky solution and relies + // implicitly on how `extension_set::create_op_ctxs` works. + let last_deno_core_op_id = crate::ops_builtin::BUILTIN_OPS.len() - 1; + let synthetic_module_exports = create_exports_for_ops_virtual_module( - &context_state.op_ctxs, + &context_state.op_ctxs[last_deno_core_op_id..], scope, global, ); diff --git a/core/runtime/tests/jsrealm.rs b/core/runtime/tests/jsrealm.rs index 3df62a702..e8425acb4 100644 --- a/core/runtime/tests/jsrealm.rs +++ b/core/runtime/tests/jsrealm.rs @@ -29,7 +29,7 @@ fn test_set_format_exception_callback_realms() { "", format!( r#" - Deno.core.ops.op_set_format_exception_callback((error) => {{ + Deno.core.setFormatExceptionCallback((error) => {{ return `{realm_name} / ${{error}}`; }}); "# diff --git a/core/runtime/tests/misc.rs b/core/runtime/tests/misc.rs index 36e453d90..db2a8f8e9 100644 --- a/core/runtime/tests/misc.rs +++ b/core/runtime/tests/misc.rs @@ -376,8 +376,8 @@ async fn wasm_streaming_op_invocation_in_import() { runtime.execute_script_static("setup.js", r#" Deno.core.setWasmStreamingCallback((source, rid) => { - Deno.core.ops.op_wasm_streaming_set_url(rid, "file:///foo.wasm"); - Deno.core.ops.op_wasm_streaming_feed(rid, source); + Deno.core.wasmStreamingSetUrl(rid, "file:///foo.wasm"); + Deno.core.wasmStreamingFeed(rid, source); Deno.core.close(rid); }); "#).unwrap(); @@ -826,7 +826,7 @@ async fn test_promise_rejection_handler_generic( function throwError() { throw new Error("boom"); } - const { op_void_async, op_void_async_deferred } = Deno.core.ensureFastOps(); + const { opVoidAsync, opVoidAsyncDeferred } = Deno.core; if (test != "no_handler") { Deno.core.setUnhandledPromiseRejectionHandler((promise, rejection) => { if (test.startsWith("exception_")) { @@ -841,9 +841,9 @@ async fn test_promise_rejection_handler_generic( } if (test != "no_reject") { if (test.startsWith("async_op_eager_")) { - op_void_async().then(() => { Deno.core.ops.op_breakpoint(); throw new Error("fail") }); + opVoidAsync().then(() => { Deno.core.ops.op_breakpoint(); throw new Error("fail") }); } else if (test.startsWith("async_op_deferred_")) { - op_void_async_deferred().then(() => { Deno.core.ops.op_breakpoint(); throw new Error("fail") }); + opVoidAsyncDeferred().then(() => { Deno.core.ops.op_breakpoint(); throw new Error("fail") }); } else if (test.startsWith("throw_")) { Deno.core.ops.op_breakpoint(); throw new Error("fail"); diff --git a/core/runtime/tests/ops.rs b/core/runtime/tests/ops.rs index ed52a6fb4..7c04f60c8 100644 --- a/core/runtime/tests/ops.rs +++ b/core/runtime/tests/ops.rs @@ -494,8 +494,8 @@ await import('./mod.js'); Url::parse("http://x/mod.js").unwrap(), ascii_str!( r#" -const { op_void_async_deferred } = Deno.core.ensureFastOps(); -await op_void_async_deferred(); +const { opVoidAsyncDeferred } = Deno.core; +await opVoidAsyncDeferred(); "# ), ), diff --git a/core/runtime/tests/snapshot.rs b/core/runtime/tests/snapshot.rs index 32676eb74..75b33983f 100644 --- a/core/runtime/tests/snapshot.rs +++ b/core/runtime/tests/snapshot.rs @@ -75,7 +75,7 @@ fn test_snapshot_callbacks() { Deno.core.setMacrotaskCallback(() => { return true; }); - Deno.core.ops.op_set_format_exception_callback(()=> { + Deno.core.setFormatExceptionCallback(()=> { return null; }) Deno.core.setUnhandledPromiseRejectionHandler(() => {