Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tagged template creation #2925

Merged
merged 4 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions boa_ast/src/expression/tagged_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct TaggedTemplate {
raws: Box<[Sym]>,
cookeds: Box<[Option<Sym>]>,
exprs: Box<[Expression]>,
identifier: u64,
}

impl TaggedTemplate {
Expand All @@ -33,12 +34,14 @@ impl TaggedTemplate {
raws: Box<[Sym]>,
cookeds: Box<[Option<Sym>]>,
exprs: Box<[Expression]>,
identifier: u64,
) -> Self {
Self {
tag: tag.into(),
raws,
cookeds,
exprs,
identifier,
}
}

Expand Down Expand Up @@ -69,6 +72,13 @@ impl TaggedTemplate {
pub const fn exprs(&self) -> &[Expression] {
&self.exprs
}

/// Gets the unique identifier of the template.
#[inline]
#[must_use]
pub const fn identifier(&self) -> u64 {
self.identifier
}
}

impl ToInternedString for TaggedTemplate {
Expand Down
1 change: 1 addition & 0 deletions boa_engine/src/builtins/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl Eval {
// c. If script Contains ScriptBody is false, return undefined.
// d. Let body be the ScriptBody of script.
let mut parser = Parser::new(Source::from_bytes(&x));
parser.set_identifier(context.next_parser_identifier());
if strict {
parser.set_strict();
}
Expand Down
25 changes: 11 additions & 14 deletions boa_engine/src/bytecompiler/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use boa_ast::{
},
Expression,
};
use boa_interner::Sym;

impl ByteCompiler<'_, '_> {
fn compile_literal(&mut self, lit: &AstLiteral, use_expr: bool) {
Expand Down Expand Up @@ -255,31 +254,29 @@ impl ByteCompiler<'_, '_> {
}
}

self.emit_opcode(Opcode::PushNewArray);
for cooked in template.cookeds() {
let site = template.identifier() as u32;
let count = template.cookeds().len() as u32;

let index = self.next_opcode_location();
self.emit(Opcode::TemplateLookup, &[Self::DUMMY_ADDRESS, site]);
let jump_label = Label { index };

for (cooked, raw) in template.cookeds().iter().zip(template.raws()) {
if let Some(cooked) = cooked {
self.emit_push_literal(Literal::String(
self.interner().resolve_expect(*cooked).into_common(false),
));
} else {
self.emit_opcode(Opcode::PushUndefined);
}
self.emit_opcode(Opcode::PushValueToArray);
}
self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::Dup);

self.emit_opcode(Opcode::PushNewArray);
for raw in template.raws() {
self.emit_push_literal(Literal::String(
self.interner().resolve_expect(*raw).into_common(false),
));
self.emit_opcode(Opcode::PushValueToArray);
}

let index = self.get_or_insert_name(Sym::RAW.into());
self.emit(Opcode::SetPropertyByName, &[index]);
self.emit(Opcode::Pop, &[]);
self.emit(Opcode::TemplateCreate, &[site, count]);

self.patch_jump(jump_label);

for expr in template.exprs() {
self.compile_expr(expr, true);
Expand Down
11 changes: 11 additions & 0 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ pub struct Context<'host> {

optimizer_options: OptimizerOptions,
root_shape: SharedShape,

/// Unique identifier for each parser instance used during the context lifetime.
parser_identifier: usize,
}

impl std::fmt::Debug for Context<'_> {
Expand Down Expand Up @@ -656,6 +659,13 @@ impl Context<'_> {
std::mem::swap(&mut self.realm, realm);
}

/// Get and increment the parser identifier.
Razican marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) fn next_parser_identifier(&mut self) -> usize {
let identifier = self.parser_identifier;
self.parser_identifier += 1;
identifier
}

/// `CanDeclareGlobalFunction ( N )`
///
/// More information:
Expand Down Expand Up @@ -1025,6 +1035,7 @@ impl<'icu, 'hooks, 'queue> ContextBuilder<'icu, 'hooks, 'queue> {
}),
optimizer_options: OptimizerOptions::OPTIMIZE_ALL,
root_shape,
parser_identifier: 0,
};

builtins::set_default_global_bindings(&mut context)?;
Expand Down
16 changes: 13 additions & 3 deletions boa_engine/src/realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
//!
//! A realm is represented in this implementation as a Realm struct with the fields specified from the spec.

use std::fmt;

use crate::{
context::{intrinsics::Intrinsics, HostHooks},
environments::DeclarativeEnvironment,
object::{shape::shared_shape::SharedShape, JsObject},
};
use boa_gc::{Finalize, Gc, Trace};
use boa_gc::{Finalize, Gc, GcRefCell, Trace};
use boa_profiler::Profiler;
use rustc_hash::FxHashMap;
use std::fmt;

/// Representation of a Realm.
///
Expand Down Expand Up @@ -49,6 +49,7 @@ struct Inner {
environment: Gc<DeclarativeEnvironment>,
global_object: JsObject,
global_this: JsObject,
template_map: GcRefCell<FxHashMap<u32, JsObject>>,
}

impl Realm {
Expand All @@ -69,6 +70,7 @@ impl Realm {
environment: Gc::new(DeclarativeEnvironment::new_global()),
global_object,
global_this,
template_map: GcRefCell::new(FxHashMap::default()),
}),
};

Expand Down Expand Up @@ -103,4 +105,12 @@ impl Realm {
bindings.resize(binding_number, None);
}
}

pub(crate) fn push_template(&self, site: u32, template: JsObject) {
self.inner.template_map.borrow_mut().insert(site, template);
}

pub(crate) fn lookup_template(&self, site: u32) -> Option<JsObject> {
self.inner.template_map.borrow().get(&site).cloned()
}
}
4 changes: 3 additions & 1 deletion boa_engine/src/vm/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,9 @@ impl CodeBlock {
| Opcode::LoopStart
| Opcode::TryStart
| Opcode::AsyncGeneratorNext
| Opcode::GeneratorAsyncDelegateNext => {
| Opcode::GeneratorAsyncDelegateNext
| Opcode::TemplateLookup
| Opcode::TemplateCreate => {
let operand1 = self.read::<u32>(*pc);
*pc += size_of::<u32>();
let operand2 = self.read::<u32>(*pc);
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/vm/flowgraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl CodeBlock {
graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red);
graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
}
Opcode::LoopStart => {
Opcode::LoopStart | Opcode::TemplateLookup | Opcode::TemplateCreate => {
let start_address = self.read::<u32>(pc);
pc += size_of::<u32>();
let end_address = self.read::<u32>(pc);
Expand Down
17 changes: 17 additions & 0 deletions boa_engine/src/vm/opcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod rest_parameter;
mod set;
mod swap;
mod switch;
mod templates;
mod to;
mod unary_ops;
mod value;
Expand Down Expand Up @@ -79,6 +80,8 @@ pub(crate) use swap::*;
#[doc(inline)]
pub(crate) use switch::*;
#[doc(inline)]
pub(crate) use templates::*;
#[doc(inline)]
pub(crate) use to::*;
#[doc(inline)]
pub(crate) use unary_ops::*;
Expand Down Expand Up @@ -1602,6 +1605,20 @@ generate_impl! {
/// Stack: value **=>** is_object
IsObject,

/// Lookup if a tagged template object is cached and skip the creation if it is.
///
/// Operands: jump: `u32`, site: `u32`
///
/// Stack: **=>** template (if cached)
TemplateLookup,

/// Create a new tagged template object and cache it.
///
/// Operands: site: `u32`, count: `u32`
///
/// Stack: count * (cooked_value, raw_value) **=>** template
TemplateCreate,

/// No-operation instruction, does nothing.
///
/// Operands:
Expand Down
104 changes: 104 additions & 0 deletions boa_engine/src/vm/opcode/templates/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use crate::{
builtins::array::Array,
object::IntegrityLevel,
property::PropertyDescriptor,
vm::{opcode::Operation, CompletionType},
Context, JsResult,
};
use boa_macros::utf16;

/// `TemplateLookup` implements the Opcode Operation for `Opcode::TemplateLookup`
///
/// Operation:
/// - Lookup if a tagged template object is cached and skip the creation if it is.
#[derive(Debug, Clone, Copy)]
pub(crate) struct TemplateLookup;

impl Operation for TemplateLookup {
const NAME: &'static str = "TemplateLookup";
const INSTRUCTION: &'static str = "INST - TemplateLookup";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let jump = context.vm.read::<u32>();
let site = context.vm.read::<u32>();

if let Some(template) = context.realm().lookup_template(site) {
context.vm.push(template);
context.vm.frame_mut().pc = jump as usize;
}

Ok(CompletionType::Normal)
}
}

/// `TemplateCreate` implements the Opcode Operation for `Opcode::TemplateCreate`
///
/// Operation:
/// - Create a new tagged template object and cache it.
#[derive(Debug, Clone, Copy)]
pub(crate) struct TemplateCreate;

impl Operation for TemplateCreate {
const NAME: &'static str = "TemplateCreate";
const INSTRUCTION: &'static str = "INST - TemplateCreate";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let site = context.vm.read::<u32>();
let count = context.vm.read::<u32>();

let template =
Array::array_create(count.into(), None, context).expect("cannot fail per spec");
let raw_obj =
Array::array_create(count.into(), None, context).expect("cannot fail per spec");

for index in (0..count).rev() {
let raw_value = context.vm.pop();
let cooked_value = context.vm.pop();
template
.define_property_or_throw(
index,
PropertyDescriptor::builder()
.value(cooked_value)
.writable(false)
.enumerable(true)
.configurable(false),
context,
)
.expect("should not fail on new array");
raw_obj
.define_property_or_throw(
index,
PropertyDescriptor::builder()
.value(raw_value)
.writable(false)
.enumerable(true)
.configurable(false),
context,
)
.expect("should not fail on new array");
}

raw_obj
.set_integrity_level(IntegrityLevel::Frozen, context)
.expect("should never fail per spec");
template
.define_property_or_throw(
utf16!("raw"),
PropertyDescriptor::builder()
.value(raw_obj)
.writable(false)
.enumerable(false)
.configurable(false),
context,
)
.expect("should never fail per spec");
template
.set_integrity_level(IntegrityLevel::Frozen, context)
.expect("should never fail per spec");

context.realm().push_template(site, template.clone());

context.vm.push(template);
Ok(CompletionType::Normal)
}
}
17 changes: 17 additions & 0 deletions boa_parser/src/parser/cursor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ pub(super) struct Cursor<R> {

/// Indicate if the cursor is used in `JSON.parse`.
json_parse: bool,

/// A unique identifier for each parser instance.
/// This is used to generate unique identifiers tagged template literals.
identifier: usize,
}

impl<R> Cursor<R>
Expand All @@ -50,6 +54,7 @@ where
private_environment_root_index: 0,
arrow: false,
json_parse: false,
identifier: 0,
}
}

Expand Down Expand Up @@ -169,6 +174,18 @@ where
self.private_environment_nested_index != 0
}

/// Set the identifier of the cursor.
#[inline]
pub(super) fn set_identifier(&mut self, identifier: usize) {
self.identifier = identifier;
}

/// Get the identifier of the cursor.
#[inline]
pub(super) const fn identifier(&self) -> usize {
self.identifier
}

/// Returns an error if the next token is not of kind `kind`.
pub(super) fn expect<K>(
&mut self,
Expand Down
Loading