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

refactor(web): use encoding_rs for text encoding (#10844)

This commit removes all JS based text encoding / text decoding. Instead
encoding now happens in Rust via encoding_rs (already in tree). This
implementation retains stream support, but adds the last missing
encodings. We are incredibly close to 100% WPT on text encoding now.

This should reduce our baseline heap by quite a bit.
This commit is contained in:
Luca Casonato 2021-06-05 23:10:07 +02:00 committed by GitHub
parent bb0c90cadb
commit c73ef5fa14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 641 additions and 4705 deletions

3
Cargo.lock generated
View file

@ -783,8 +783,11 @@ dependencies = [
name = "deno_web"
version = "0.38.1"
dependencies = [
"base64 0.13.0",
"deno_core",
"encoding_rs",
"futures",
"serde",
]
[[package]]

View file

@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
at blob:null/[WILDCARD]:1:0
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at blob:null/[WILDCARD]:1:0
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
at data:application/javascript;base64,[WILDCARD]:1:0
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:0
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -3,4 +3,4 @@ await import("https://example.com/some/file.ts");
^
at async http://localhost:4545/cli/tests/workers/dynamic_remote.ts:2:1
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -1,4 +1,4 @@
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
at http://localhost:4545/cli/tests/workers/static_remote.ts:2:0
error: Uncaught (in promise) Error: Unhandled error event reached main worker.
at Worker.#poll (deno:runtime/js/11_workers.js:245:23)
at Worker.#poll (deno:runtime/js/11_workers.js:243:23)

View file

@ -36,6 +36,10 @@ pub fn type_error(message: impl Into<Cow<'static, str>>) -> AnyError {
custom_error("TypeError", message)
}
pub fn range_error(message: impl Into<Cow<'static, str>>) -> AnyError {
custom_error("RangeError", message)
}
pub fn invalid_hostname(hostname: &str) -> AnyError {
type_error(format!("Invalid hostname: '{}'", hostname))
}

View file

@ -10,15 +10,15 @@ declare namespace Deno {
/** Call an op in Rust, and synchronously receive the result. */
function opSync(
opName: string,
args?: any,
zeroCopy?: Uint8Array,
a?: any,
b?: any,
): any;
/** Call an op in Rust, and asynchronously receive the result. */
function opAsync(
opName: string,
args?: any,
zeroCopy?: Uint8Array,
a?: any,
b?: any,
): Promise<any>;
/**
@ -38,5 +38,8 @@ declare namespace Deno {
/** Get heap stats for current isolate/worker */
function heapStats(): Record<string, number>;
/** Encode a string to its Uint8Array representation. */
function encode(input: string): Uint8Array;
}
}

View file

@ -11,7 +11,8 @@
/// <reference lib="esnext" />
"use strict";
((_window) => {
((window) => {
const core = window.Deno.core;
const webidl = globalThis.__bootstrap.webidl;
const { Blob, File, _byteSequence } = globalThis.__bootstrap.file;
@ -240,8 +241,6 @@
webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
const encoder = new TextEncoder();
class MultipartBuilder {
/**
* @param {FormData} formData
@ -270,7 +269,7 @@
} else this.#writeField(name, value);
}
this.chunks.push(encoder.encode(`\r\n--${this.boundary}--`));
this.chunks.push(core.encode(`\r\n--${this.boundary}--`));
let totalLength = 0;
for (const chunk of this.chunks) {
@ -309,7 +308,7 @@
}
buf += `\r\n`;
this.chunks.push(encoder.encode(buf));
this.chunks.push(core.encode(buf));
}
/**
@ -356,7 +355,7 @@
*/
#writeField(field, value) {
this.#writeFieldHeaders(field);
this.chunks.push(encoder.encode(this.#normalizeNewlines(value)));
this.chunks.push(core.encode(this.#normalizeNewlines(value)));
}
/**
@ -428,7 +427,6 @@
const LF = "\n".codePointAt(0);
const CR = "\r".codePointAt(0);
const decoder = new TextDecoder("utf-8");
class MultipartParser {
/**
@ -442,7 +440,7 @@
this.boundary = `--${boundary}`;
this.body = body;
this.boundaryChars = encoder.encode(this.boundary);
this.boundaryChars = core.encode(this.boundary);
}
/**
@ -539,7 +537,7 @@
});
formData.append(name, blob, filename);
} else {
formData.append(name, decoder.decode(content));
formData.append(name, core.decode(content));
}
}
} else if (state === 5 && isNewLine) {

View file

@ -223,8 +223,6 @@
return Object.defineProperties(prototype.prototype, mixin);
}
const decoder = new TextDecoder();
/**
* https://fetch.spec.whatwg.org/#concept-body-package-data
* @param {Uint8Array} bytes
@ -263,14 +261,12 @@
throw new TypeError("Missing content type");
}
case "JSON":
return JSON.parse(decoder.decode(bytes));
return JSON.parse(core.decode(bytes));
case "text":
return decoder.decode(bytes);
return core.decode(bytes);
}
}
const encoder = new TextEncoder();
/**
* @param {BodyInit} object
* @returns {{body: InnerBody, contentType: string | null}}
@ -305,10 +301,10 @@
length = res.body.byteLength;
contentType = res.contentType;
} else if (object instanceof URLSearchParams) {
source = encoder.encode(object.toString());
source = core.encode(object.toString());
contentType = "application/x-www-form-urlencoded;charset=UTF-8";
} else if (typeof object === "string") {
source = encoder.encode(object);
source = core.encode(object);
contentType = "text/plain;charset=UTF-8";
} else if (object instanceof ReadableStream) {
stream = object;

View file

@ -12,6 +12,7 @@
"use strict";
((window) => {
const core = window.Deno.core;
const webidl = window.__bootstrap.webidl;
// TODO(lucacasonato): this needs to not be hardcoded and instead depend on
@ -85,9 +86,6 @@
return finalBytes;
}
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
/** @typedef {BufferSource | Blob | string} BlobPart */
/**
@ -116,7 +114,7 @@
if (endings == "native") {
s = convertLineEndingsToNative(s);
}
bytesArrays.push(utf8Encoder.encode(s));
bytesArrays.push(core.encode(s));
} else {
throw new TypeError("Unreachable code (invalild element type)");
}
@ -276,7 +274,7 @@
async text() {
webidl.assertBranded(this, Blob);
const buffer = await this.arrayBuffer();
return utf8Decoder.decode(buffer);
return core.decode(new Uint8Array(buffer));
}
/**

View file

@ -14,9 +14,9 @@
((window) => {
const webidl = window.__bootstrap.webidl;
const { decode } = window.__bootstrap.encoding;
const { forgivingBase64Encode } = window.__bootstrap.infra;
const { decode, TextDecoder } = window.__bootstrap.encoding;
const { parseMimeType } = window.__bootstrap.mimesniff;
const base64 = window.__bootstrap.base64;
const state = Symbol("[[state]]");
const result = Symbol("[[result]]");
@ -168,7 +168,7 @@
case "DataUrl": {
const mediaType = blob.type || "application/octet-stream";
this[result] = `data:${mediaType};base64,${
base64.fromByteArray(bytes)
forgivingBase64Encode(bytes)
}`;
break;
}

View file

@ -8,6 +8,8 @@
"use strict";
((window) => {
const core = Deno.core;
const ASCII_DIGIT = ["\u0030-\u0039"];
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
@ -178,6 +180,22 @@
return { result: input.substring(positionStart, position + 1), position };
}
/**
* @param {Uint8Array} data
* @returns {string}
*/
function forgivingBase64Encode(data) {
return core.opSync("op_base64_encode", data);
}
/**
* @param {string} data
* @returns {Uint8Array}
*/
function forgivingBase64Decode(data) {
return core.opSync("op_base64_decode", data);
}
window.__bootstrap.infra = {
collectSequenceOfCodepoints,
ASCII_DIGIT,
@ -199,5 +217,7 @@
byteUpperCase,
byteLowerCase,
collectHttpQuotedString,
forgivingBase64Encode,
forgivingBase64Decode,
};
})(globalThis);

View file

@ -0,0 +1,62 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// @ts-check
/// <reference path="../webidl/internal.d.ts" />
/// <reference path="../web/internal.d.ts" />
/// <reference lib="esnext" />
"use strict";
((window) => {
const webidl = window.__bootstrap.webidl;
const {
forgivingBase64Encode,
forgivingBase64Decode,
} = window.__bootstrap.infra;
/**
* @param {string} data
* @returns {string}
*/
function atob(data) {
data = webidl.converters.DOMString(data, {
prefix: "Failed to execute 'atob'",
context: "Argument 1",
});
const uint8Array = forgivingBase64Decode(data);
const result = [...uint8Array]
.map((byte) => String.fromCharCode(byte))
.join("");
return result;
}
/**
* @param {string} data
* @returns {string}
*/
function btoa(data) {
const prefix = "Failed to execute 'btoa'";
webidl.requiredArguments(arguments.length, 1, { prefix });
data = webidl.converters.DOMString(data, {
prefix,
context: "Argument 1",
});
const byteArray = [...data].map((char) => {
const charCode = char.charCodeAt(0);
if (charCode > 0xff) {
throw new DOMException(
"The string to be encoded contains characters outside of the Latin1 range.",
"InvalidCharacterError",
);
}
return charCode;
});
return forgivingBase64Encode(Uint8Array.from(byteArray));
}
window.__bootstrap.base64 = {
atob,
btoa,
};
})(globalThis);

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,10 @@ repository = "https://github.com/denoland/deno"
path = "lib.rs"
[dependencies]
base64 = "0.13.0"
deno_core = { version = "0.88.1", path = "../../core" }
encoding_rs = "0.8.28"
serde = "1.0"
[dev-dependencies]
futures = "0.3.15"

View file

@ -43,6 +43,8 @@ declare namespace globalThis {
result: string;
position: number;
};
forgivingBase64Encode(data: Uint8Array): string;
forgivingBase64Decode(data: string): Uint8Array;
};
declare namespace mimesniff {
@ -65,9 +67,8 @@ declare namespace globalThis {
};
declare var base64: {
byteLength(b64: string): number;
toByteArray(b64: string): Uint8Array;
fromByteArray(uint8: Uint8Array): string;
atob(data: string): string;
btoa(data: string): string;
};
}
}

View file

@ -177,20 +177,32 @@ declare function atob(s: string): string;
*/
declare function btoa(s: string): string;
declare interface TextDecoderOptions {
fatal?: boolean;
ignoreBOM?: boolean;
}
declare interface TextDecodeOptions {
stream?: boolean;
}
declare class TextDecoder {
constructor(label?: string, options?: TextDecoderOptions);
/** Returns encoding's name, lowercased. */
readonly encoding: string;
/** Returns `true` if error mode is "fatal", and `false` otherwise. */
readonly fatal: boolean;
/** Returns `true` if ignore BOM flag is set, and `false` otherwise. */
readonly ignoreBOM = false;
constructor(
label?: string,
options?: { fatal?: boolean; ignoreBOM?: boolean },
);
/** Returns the result of running encoding's decoder. */
decode(input?: BufferSource, options?: { stream?: boolean }): string;
readonly [Symbol.toStringTag]: string;
decode(input?: BufferSource, options?: TextDecodeOptions): string;
}
declare interface TextEncoderEncodeIntoResult {
read: number;
written: number;
}
declare class TextEncoder {
@ -198,11 +210,7 @@ declare class TextEncoder {
readonly encoding = "utf-8";
/** Returns the result of running UTF-8's encoder. */
encode(input?: string): Uint8Array;
encodeInto(
input: string,
dest: Uint8Array,
): { read: number; written: number };
readonly [Symbol.toStringTag]: string;
encodeInto(input: string, dest: Uint8Array): TextEncoderEncodeIntoResult;
}
/** A controller object that allows you to abort one or more DOM requests as and

View file

@ -1,10 +1,28 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_core::error::bad_resource_id;
use deno_core::error::range_error;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::include_js_files;
use deno_core::op_sync;
use deno_core::Extension;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
use encoding_rs::CoderResult;
use encoding_rs::Decoder;
use encoding_rs::DecoderResult;
use encoding_rs::Encoding;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt;
use std::path::PathBuf;
use std::usize;
/// Load and execute the javascript code.
pub fn init() -> Extension {
@ -17,12 +35,235 @@ pub fn init() -> Extension {
"02_event.js",
"03_abort_signal.js",
"04_global_interfaces.js",
"05_base64.js",
"08_text_encoding.js",
"12_location.js",
))
.ops(vec![
("op_base64_decode", op_sync(op_base64_decode)),
("op_base64_encode", op_sync(op_base64_encode)),
(
"op_encoding_normalize_label",
op_sync(op_encoding_normalize_label),
),
("op_encoding_new_decoder", op_sync(op_encoding_new_decoder)),
("op_encoding_decode", op_sync(op_encoding_decode)),
("op_encoding_encode_into", op_sync(op_encoding_encode_into)),
])
.build()
}
fn op_base64_decode(
_state: &mut OpState,
input: String,
_: (),
) -> Result<ZeroCopyBuf, AnyError> {
let mut input: &str = &input.replace(|c| char::is_ascii_whitespace(&c), "");
// "If the length of input divides by 4 leaving no remainder, then:
// if input ends with one or two U+003D EQUALS SIGN (=) characters,
// remove them from input."
if input.len() % 4 == 0 {
if input.ends_with("==") {
input = &input[..input.len() - 2]
} else if input.ends_with('=') {
input = &input[..input.len() - 1]
}
}
// "If the length of input divides by 4 leaving a remainder of 1,
// throw an InvalidCharacterError exception and abort these steps."
if input.len() % 4 == 1 {
return Err(
DomExceptionInvalidCharacterError::new("Failed to decode base64.").into(),
);
}
if input
.chars()
.any(|c| c != '+' && c != '/' && !c.is_alphanumeric())
{
return Err(
DomExceptionInvalidCharacterError::new(
"Failed to decode base64: invalid character",
)
.into(),
);
}
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
.decode_allow_trailing_bits(true);
let out = base64::decode_config(&input, cfg).map_err(|err| {
DomExceptionInvalidCharacterError::new(&format!(
"Failed to decode base64: {:?}",
err
))
})?;
Ok(ZeroCopyBuf::from(out))
}
fn op_base64_encode(
_state: &mut OpState,
s: ZeroCopyBuf,
_: (),
) -> Result<String, AnyError> {
let cfg = base64::Config::new(base64::CharacterSet::Standard, true)
.decode_allow_trailing_bits(true);
let out = base64::encode_config(&s, cfg);
Ok(out)
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct DecoderOptions {
label: String,
ignore_bom: bool,
fatal: bool,
}
fn op_encoding_normalize_label(
_state: &mut OpState,
label: String,
_: (),
) -> Result<String, AnyError> {
let encoding = Encoding::for_label_no_replacement(label.as_bytes())
.ok_or_else(|| {
range_error(format!(
"The encoding label provided ('{}') is invalid.",
label
))
})?;
Ok(encoding.name().to_lowercase())
}
fn op_encoding_new_decoder(
state: &mut OpState,
options: DecoderOptions,
_: (),
) -> Result<ResourceId, AnyError> {
let DecoderOptions {
label,
fatal,
ignore_bom,
} = options;
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
range_error(format!(
"The encoding label provided ('{}') is invalid.",
label
))
})?;
let decoder = if ignore_bom {
encoding.new_decoder_without_bom_handling()
} else {
encoding.new_decoder_with_bom_removal()
};
let rid = state.resource_table.add(TextDecoderResource {
decoder: RefCell::new(decoder),
fatal,
});
Ok(rid)
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct DecodeOptions {
rid: ResourceId,
stream: bool,
}
fn op_encoding_decode(
state: &mut OpState,
data: ZeroCopyBuf,
options: DecodeOptions,
) -> Result<String, AnyError> {
let DecodeOptions { rid, stream } = options;
let resource = state
.resource_table
.get::<TextDecoderResource>(rid)
.ok_or_else(bad_resource_id)?;
let mut decoder = resource.decoder.borrow_mut();
let fatal = resource.fatal;
let max_buffer_length = if fatal {
decoder
.max_utf8_buffer_length_without_replacement(data.len())
.ok_or_else(|| range_error("Value too large to decode."))?
} else {
decoder
.max_utf8_buffer_length(data.len())
.ok_or_else(|| range_error("Value too large to decode."))?
};
let mut output = String::with_capacity(max_buffer_length);
if fatal {
let (result, _) =
decoder.decode_to_string_without_replacement(&data, &mut output, !stream);
match result {
DecoderResult::InputEmpty => Ok(output),
DecoderResult::OutputFull => {
Err(range_error("Provided buffer too small."))
}
DecoderResult::Malformed(_, _) => {
Err(type_error("The encoded data is not valid."))
}
}
} else {
let (result, _, _) = decoder.decode_to_string(&data, &mut output, !stream);
match result {
CoderResult::InputEmpty => Ok(output),
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
}
}
}
struct TextDecoderResource {
decoder: RefCell<Decoder>,
fatal: bool,
}
impl Resource for TextDecoderResource {
fn name(&self) -> Cow<str> {
"textDecoder".into()
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct EncodeIntoResult {
read: usize,
written: usize,
}
fn op_encoding_encode_into(
_state: &mut OpState,
input: String,
mut buffer: ZeroCopyBuf,
) -> Result<EncodeIntoResult, AnyError> {
let dst: &mut [u8] = &mut buffer;
let mut read = 0;
let mut written = 0;
for char in input.chars() {
let len = char.len_utf8();
if dst.len() < written + len {
break;
}
char.encode_utf8(&mut dst[written..]);
written += len;
if char > '\u{FFFF}' {
read += 2
} else {
read += 1
};
}
Ok(EncodeIntoResult { read, written })
}
pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
}
@ -40,17 +281,39 @@ impl DomExceptionQuotaExceededError {
}
}
#[derive(Debug)]
pub struct DomExceptionInvalidCharacterError {
pub msg: String,
}
impl DomExceptionInvalidCharacterError {
pub fn new(msg: &str) -> Self {
DomExceptionInvalidCharacterError {
msg: msg.to_string(),
}
}
}
impl fmt::Display for DomExceptionQuotaExceededError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.msg)
}
}
impl fmt::Display for DomExceptionInvalidCharacterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(&self.msg)
}
}
impl std::error::Error for DomExceptionQuotaExceededError {}
pub fn get_quota_exceeded_error_class_name(
e: &AnyError,
) -> Option<&'static str> {
impl std::error::Error for DomExceptionInvalidCharacterError {}
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
e.downcast_ref::<DomExceptionQuotaExceededError>()
.map(|_| "DOMExceptionQuotaExceededError")
.or_else(|| {
e.downcast_ref::<DomExceptionInvalidCharacterError>()
.map(|_| "DOMExceptionInvalidCharacterError")
})
}

View file

@ -241,8 +241,7 @@
sendTypedArray(new DataView(data));
} else {
const string = String(data);
const encoder = new TextEncoder();
const d = encoder.encode(string);
const d = core.encode(string);
this.#bufferedAmount += d.size;
core.opAsync("op_ws_send", {
rid: this.#rid,
@ -262,8 +261,7 @@
);
}
const encoder = new TextEncoder();
if (reason && encoder.encode(reason).byteLength > 123) {
if (reason && core.encode(reason).byteLength > 123) {
throw new DOMException(
"The close reason may not be longer than 123 bytes.",
"SyntaxError",

View file

@ -157,7 +157,7 @@ fn get_nix_error_class(error: &nix::Error) -> &'static str {
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| deno_webgpu::error::get_error_class_name(e))
.or_else(|| deno_web::get_quota_exceeded_error_class_name(e))
.or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| {
e.downcast_ref::<dlopen::Error>()

View file

@ -38,8 +38,6 @@
return core.opAsync("op_host_get_message", id);
}
const decoder = new TextDecoder();
/**
* @param {string} permission
* @return {boolean}
@ -166,7 +164,7 @@
this.#name = name;
const hasSourceCode = false;
const sourceCode = decoder.decode(new Uint8Array());
const sourceCode = core.decode(new Uint8Array());
if (
specifier.startsWith("./") || specifier.startsWith("../") ||

View file

@ -2,6 +2,7 @@
"use strict";
((window) => {
const core = window.Deno.core;
const { open, openSync } = window.__bootstrap.files;
const { readAll, readAllSync } = window.__bootstrap.io;
@ -29,8 +30,7 @@
const file = openSync(path);
try {
const contents = readAllSync(file);
const decoder = new TextDecoder();
return decoder.decode(contents);
return core.decode(contents);
} finally {
file.close();
}
@ -40,8 +40,7 @@
const file = await open(path);
try {
const contents = await readAll(file);
const decoder = new TextDecoder();
return decoder.decode(contents);
return core.decode(contents);
} finally {
file.close();
}

View file

@ -5,7 +5,6 @@
const { isatty } = window.__bootstrap.tty;
const LF = "\n".charCodeAt(0);
const CR = "\r".charCodeAt(0);
const decoder = new TextDecoder();
const core = window.Deno.core;
function alert(message = "Alert") {
@ -70,7 +69,7 @@
}
buf.push(c[0]);
}
return decoder.decode(new Uint8Array(buf));
return core.decode(new Uint8Array(buf));
}
window.__bootstrap.prompt = {

View file

@ -16,6 +16,8 @@ delete Object.prototype.__proto__;
const errorStack = window.__bootstrap.errorStack;
const os = window.__bootstrap.os;
const timers = window.__bootstrap.timers;
const base64 = window.__bootstrap.base64;
const encoding = window.__bootstrap.encoding;
const Console = window.__bootstrap.console.Console;
const worker = window.__bootstrap.worker;
const signals = window.__bootstrap.signals;
@ -198,6 +200,12 @@ delete Object.prototype.__proto__;
return new DOMException(msg, "NotSupported");
},
);
core.registerErrorBuilder(
"DOMExceptionInvalidCharacterError",
function DOMExceptionInvalidCharacterError(msg) {
return new DOMException(msg, "InvalidCharacterError");
},
);
}
class Navigator {
@ -277,8 +285,8 @@ delete Object.prototype.__proto__;
),
Request: util.nonEnumerable(fetch.Request),
Response: util.nonEnumerable(fetch.Response),
TextDecoder: util.nonEnumerable(TextDecoder),
TextEncoder: util.nonEnumerable(TextEncoder),
TextDecoder: util.nonEnumerable(encoding.TextDecoder),
TextEncoder: util.nonEnumerable(encoding.TextEncoder),
TransformStream: util.nonEnumerable(streams.TransformStream),
URL: util.nonEnumerable(url.URL),
URLSearchParams: util.nonEnumerable(url.URLSearchParams),
@ -295,8 +303,8 @@ delete Object.prototype.__proto__;
TransformStreamDefaultController: util.nonEnumerable(
streams.TransformStreamDefaultController,
),
atob: util.writable(atob),
btoa: util.writable(btoa),
atob: util.writable(base64.atob),
btoa: util.writable(base64.btoa),
clearInterval: util.writable(timers.clearInterval),
clearTimeout: util.writable(timers.clearTimeout),
console: util.writable(

View file

@ -187,18 +187,6 @@
"encodeInto() and a detached output buffer"
],
"idlharness.any.html": [
"TextDecoder interface: existence and properties of interface object",
"TextDecoder interface: operation decode(optional BufferSource, optional TextDecodeOptions)",
"TextDecoder interface: attribute encoding",
"TextDecoder interface: attribute fatal",
"TextDecoder interface: attribute ignoreBOM",
"TextDecoder interface: new TextDecoder() must inherit property \"fatal\" with the proper type",
"TextDecoder interface: new TextDecoder() must inherit property \"ignoreBOM\" with the proper type",
"TextEncoder interface: existence and properties of interface object",
"TextEncoder interface: operation encode(optional USVString)",
"TextEncoder interface: operation encodeInto(USVString, Uint8Array)",
"TextEncoder interface: attribute encoding",
"TextEncoder interface: new TextEncoder() must inherit property \"encoding\" with the proper type",
"TextDecoderStream interface: existence and properties of interface object",
"TextDecoderStream interface object length",
"TextDecoderStream interface object name",
@ -216,7 +204,7 @@
"TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property",
"TextEncoderStream interface: attribute encoding"
],
"iso-2022-jp-decoder.any.html": false,
"iso-2022-jp-decoder.any.html": true,
"legacy-mb-schinese": {
"gb18030": {
"gb18030-decoder.any.html": true
@ -237,11 +225,12 @@
"decode-utf8.any.html": false,
"encode-bad-chunks.any.html": false,
"encode-utf8.any.html": false,
"readable-writable-properties.any.html": false
"readable-writable-properties.any.html": false,
"realms.window.html": false
},
"textdecoder-arguments.any.html": true,
"textdecoder-byte-order-marks.any.html": true,
"textdecoder-copy.any.html": false,
"textdecoder-copy.any.html": true,
"textdecoder-fatal-single-byte.any.html?1-1000": true,
"textdecoder-fatal-single-byte.any.html?1001-2000": true,
"textdecoder-fatal-single-byte.any.html?2001-3000": true,
@ -250,46 +239,23 @@
"textdecoder-fatal-single-byte.any.html?5001-6000": true,
"textdecoder-fatal-single-byte.any.html?6001-7000": true,
"textdecoder-fatal-single-byte.any.html?7001-last": true,
"textdecoder-fatal-streaming.any.html": [
"Fatal flag, streaming cases"
],
"textdecoder-fatal-streaming.any.html": true,
"textdecoder-fatal.any.html": true,
"textdecoder-ignorebom.any.html": true,
"textdecoder-labels.any.html": [
"cseucpkdfmtjapanese => EUC-JP",
"euc-jp => EUC-JP",
"x-euc-jp => EUC-JP",
"csiso2022jp => ISO-2022-JP",
"iso-2022-jp => ISO-2022-JP",
"csshiftjis => Shift_JIS",
"ms932 => Shift_JIS",
"ms_kanji => Shift_JIS",
"shift-jis => Shift_JIS",
"shift_jis => Shift_JIS",
"sjis => Shift_JIS",
"windows-31j => Shift_JIS",
"x-sjis => Shift_JIS",
"cseuckr => EUC-KR",
"csksc56011987 => EUC-KR",
"euc-kr => EUC-KR",
"iso-ir-149 => EUC-KR",
"korean => EUC-KR",
"ks_c_5601-1987 => EUC-KR",
"ks_c_5601-1989 => EUC-KR",
"ksc5601 => EUC-KR",
"ksc_5601 => EUC-KR",
"windows-949 => EUC-KR",
"x-user-defined => x-user-defined"
"unicode11utf8 => UTF-8",
"unicode20utf8 => UTF-8",
"x-unicode20utf8 => UTF-8",
"unicodefffe => UTF-16BE",
"csunicode => UTF-16LE",
"iso-10646-ucs-2 => UTF-16LE",
"ucs-2 => UTF-16LE",
"unicode => UTF-16LE",
"unicodefeff => UTF-16LE"
],
"textdecoder-streaming.any.html": true,
"textdecoder-utf16-surrogates.any.html": true,
"textencoder-constructor-non-utf.any.html": [
"Encoding argument supported for decode: EUC-JP",
"Encoding argument supported for decode: ISO-2022-JP",
"Encoding argument supported for decode: Shift_JIS",
"Encoding argument supported for decode: EUC-KR",
"Encoding argument supported for decode: x-user-defined"
],
"textencoder-constructor-non-utf.any.html": true,
"textencoder-utf16-surrogates.any.html": true,
"unsupported-encodings.any.html": false
},