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

fix(core): panic at build time if extension code contains anything other than 7-bit ASCII (#18372)

This will improve diagnostics and catch any non-ASCII extension code
early.

This will use `debug_assert!` rather than `assert!` to avoid runtime
costs, and ensures (in debug_assert mode only) that all extension source
files are ASCII as we load them.
This commit is contained in:
Matt Mastracci 2023-03-23 16:00:46 -06:00 committed by GitHub
parent e8348231df
commit ad77ba0f7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 44 additions and 29 deletions

View file

@ -128952,7 +128952,7 @@ ${lanes.join("\n")}
// Adding or removing imports from node could change the outcome of that guess, so could change the suggestions list.
typeAcquisitionEnabled && consumesNodeCoreModules(oldSourceFile) !== consumesNodeCoreModules(newSourceFile) || // Module agumentation and ambient module changes can add or remove exports available to be auto-imported.
// Changes elsewhere in the file can change the *type* of an export in a module augmentation,
// but type info is gathered in getCompletionEntryDetails, which doesnt use the cache.
// but type info is gathered in getCompletionEntryDetails, which doesn't use the cache.
!arrayIsEqualTo(oldSourceFile.moduleAugmentations, newSourceFile.moduleAugmentations) || !ambientModuleDeclarationsAreEqual(oldSourceFile, newSourceFile)) {
cache.clear();
return true;
@ -137732,7 +137732,7 @@ ${lanes.join("\n")}
function symbolReferenceIsAlsoMissingAwait(reference, diagnostics, sourceFile, checker) {
const errorNode = isPropertyAccessExpression(reference.parent) ? reference.parent.name : isBinaryExpression(reference.parent) ? reference.parent : reference;
const diagnostic = find(diagnostics, (diagnostic2) => diagnostic2.start === errorNode.getStart(sourceFile) && diagnostic2.start + diagnostic2.length === errorNode.getEnd());
return diagnostic && contains(errorCodes3, diagnostic.code) || // A Promise is usually not correct in a binary expression (its not valid
return diagnostic && contains(errorCodes3, diagnostic.code) || // A Promise is usually not correct in a binary expression (it's not valid
// in an arithmetic expression and an equality comparison seems unusual),
// but if the other side of the binary expression has an error, the side
// is typed `any` which will squash the error that would identify this
@ -153491,7 +153491,7 @@ ${lanes.join("\n")}
*/
this.markSeenReExportRHS = nodeSeenTracker();
this.symbolIdToReferences = [];
// Source file ID → symbol ID → Whether the symbol has been searched for in the source file.
// Source file ID -> symbol ID -> Whether the symbol has been searched for in the source file.
this.sourceFileToSeenSymbols = [];
}
includesSourceFile(sourceFile) {
@ -162610,7 +162610,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
if (start !== end) {
const textSpan = createTextSpanFromBounds(start, end);
if (!selectionRange || // Skip ranges that are identical to the parent
!textSpansEqual(textSpan, selectionRange.textSpan) && // Skip ranges that dont contain the original position
!textSpansEqual(textSpan, selectionRange.textSpan) && // Skip ranges that don't contain the original position
textSpanIntersectsWithPosition(textSpan, pos)) {
selectionRange = { textSpan, ...selectionRange && { parent: selectionRange } };
}

View file

@ -23,23 +23,43 @@ pub enum ExtensionFileSourceCode {
LoadedFromFsDuringSnapshot(PathBuf),
}
impl ExtensionFileSourceCode {
pub fn load(&self) -> Result<ModuleCode, Error> {
match self {
ExtensionFileSourceCode::IncludedInBinary(code) => Ok((*code).into()),
ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => {
let msg = || format!("Failed to read \"{}\"", path.display());
Ok(std::fs::read_to_string(path).with_context(msg)?.into())
}
}
}
}
#[derive(Clone, Debug)]
pub struct ExtensionFileSource {
pub specifier: &'static str,
pub code: ExtensionFileSourceCode,
}
impl ExtensionFileSource {
fn find_non_ascii(s: &str) -> String {
s.chars().filter(|c| !c.is_ascii()).collect::<String>()
}
pub fn load(&self) -> Result<ModuleCode, Error> {
match &self.code {
ExtensionFileSourceCode::IncludedInBinary(code) => {
debug_assert!(
code.is_ascii(),
"Extension code must be 7-bit ASCII: {} (found {})",
self.specifier,
Self::find_non_ascii(code)
);
Ok((*code).into())
}
ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) => {
let msg = || format!("Failed to read \"{}\"", path.display());
let s = std::fs::read_to_string(path).with_context(msg)?;
debug_assert!(
s.is_ascii(),
"Extension code must be 7-bit ASCII: {} (found {})",
self.specifier,
Self::find_non_ascii(&s)
);
Ok(s.into())
}
}
}
}
pub type OpFnRef = v8::FunctionCallback;
pub type OpMiddlewareFn = dyn Fn(OpDecl) -> OpDecl;
pub type OpStateFn = dyn FnOnce(&mut OpState);

View file

@ -309,7 +309,7 @@ impl From<Cow<'static, [u8]>> for ModuleCode {
impl From<&'static str> for ModuleCode {
#[inline(always)]
fn from(value: &'static str) -> Self {
assert!(value.is_ascii());
debug_assert!(value.is_ascii());
ModuleCode::Static(value.as_bytes())
}
}
@ -331,7 +331,7 @@ impl From<Vec<u8>> for ModuleCode {
impl From<&'static [u8]> for ModuleCode {
#[inline(always)]
fn from(value: &'static [u8]) -> Self {
assert!(value.is_ascii());
debug_assert!(value.is_ascii());
ModuleCode::Static(value)
}
}
@ -339,7 +339,7 @@ impl From<&'static [u8]> for ModuleCode {
impl<const N: usize> From<&'static [u8; N]> for ModuleCode {
#[inline(always)]
fn from(value: &'static [u8; N]) -> Self {
assert!(value.is_ascii());
debug_assert!(value.is_ascii());
ModuleCode::Static(value)
}
}
@ -583,7 +583,7 @@ impl ModuleLoader for ExtModuleLoader {
let result = if let Some(load_callback) = &self.maybe_load_callback {
load_callback(file_source)
} else {
match file_source.code.load() {
match file_source.load() {
Ok(code) => Ok(code),
Err(err) => return futures::future::err(err).boxed_local(),
}
@ -1517,7 +1517,7 @@ impl ModuleMap {
) -> Option<v8::Local<'a, v8::String>> {
match name {
ModuleName::Static(s) => {
assert!(s.is_ascii());
debug_assert!(s.is_ascii());
v8::String::new_external_onebyte_static(scope, s.as_bytes())
}
ModuleName::NotStatic(s) => v8::String::new(scope, s),

View file

@ -751,7 +751,7 @@ impl JsRuntime {
realm.execute_script(
self.v8_isolate(),
file_source.specifier,
file_source.code.load()?,
file_source.load()?,
)?;
}
}
@ -2544,7 +2544,7 @@ impl JsRealm {
let scope = &mut self.handle_scope(isolate);
let source = Self::string_from_code(scope, &source_code).unwrap();
assert!(name.is_ascii());
debug_assert!(name.is_ascii());
let name =
v8::String::new_external_onebyte_static(scope, name.as_bytes()).unwrap();
let origin = bindings::script_origin(scope, name);

View file

@ -3,11 +3,6 @@
import { getStringWidth } from "ext:deno_node/internal/util/inspect.mjs";
// The use of Unicode characters below is the only non-comment use of non-ASCII
// Unicode characters in Node.js built-in modules. If they are ever removed or
// rewritten with \u escapes, then a test will need to be (re-)added to Node.js
// core to verify that Unicode characters work in built-ins.
// Refs: https://github.com/nodejs/node/issues/10673
const tableChars = {
middleMiddle: "\u2500",
rowMiddle: "\u253c",

View file

@ -34,7 +34,7 @@ mod startup_snapshot {
file_source.specifier
),
};
let code = file_source.code.load()?;
let code = file_source.load()?;
if !should_transpile {
return Ok(code);