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

Implement pseudo-property import.meta #2956

Merged
merged 2 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
9 changes: 6 additions & 3 deletions boa_ast/src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ pub enum Expression {
/// The `new.target` pseudo-property expression.
NewTarget,

// TODO: import.meta
/// The `import.meta` pseudo-property expression.
ImportMeta,

/// See [`Assign`].
Assign(Assign),

Expand Down Expand Up @@ -197,6 +199,7 @@ impl Expression {
Self::ImportCall(impc) => impc.to_interned_string(interner),
Self::Optional(opt) => opt.to_interned_string(interner),
Self::NewTarget => "new.target".to_owned(),
Self::ImportMeta => "import.meta".to_owned(),
Self::TaggedTemplate(tag) => tag.to_interned_string(interner),
Self::Assign(assign) => assign.to_interned_string(interner),
Self::Unary(unary) => unary.to_interned_string(interner),
Expand Down Expand Up @@ -316,7 +319,7 @@ impl VisitWith for Expression {
Self::Yield(y) => visitor.visit_yield(y),
Self::Parenthesized(e) => visitor.visit_parenthesized(e),
Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list(fpl),
Self::This | Self::NewTarget => {
Self::This | Self::NewTarget | Self::ImportMeta => {
// do nothing; can be handled as special case by visitor
ControlFlow::Continue(())
}
Expand Down Expand Up @@ -358,7 +361,7 @@ impl VisitWith for Expression {
Self::Yield(y) => visitor.visit_yield_mut(y),
Self::Parenthesized(e) => visitor.visit_parenthesized_mut(e),
Self::FormalParameterList(fpl) => visitor.visit_formal_parameter_list_mut(fpl),
Self::This | Self::NewTarget => {
Self::This | Self::NewTarget | Self::ImportMeta => {
// do nothing; can be handled as special case by visitor
ControlFlow::Continue(())
}
Expand Down
7 changes: 6 additions & 1 deletion boa_engine/src/bytecompiler/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,12 @@ impl ByteCompiler<'_, '_> {
}
Expression::NewTarget => {
if use_expr {
self.emit_opcode(Opcode::PushNewTarget);
self.emit_opcode(Opcode::NewTarget);
}
}
Expression::ImportMeta => {
if use_expr {
self.emit_opcode(Opcode::ImportMeta);
}
}
Expression::Optional(opt) => {
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ pub trait ModuleLoader {
/// [final]: https://tc39.es/ecma262/#sec-hostfinalizeimportmeta
fn init_import_meta(
&self,
_import_meta: JsObject,
_module: Module,
_import_meta: &JsObject,
_module: &Module,
_context: &mut Context<'_>,
) {
}
Expand Down
5 changes: 5 additions & 0 deletions boa_engine/src/module/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1703,6 +1703,11 @@ impl SourceTextModule {
pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<Sym, Module>> {
&self.inner.loaded_modules
}

/// Gets the import meta object of this module.
pub(crate) fn import_meta(&self) -> &GcRefCell<Option<JsObject>> {
&self.inner.import_meta
}
}

/// Abstract operation [`AsyncModuleExecutionFulfilled ( module )`][spec].
Expand Down
3 changes: 2 additions & 1 deletion boa_engine/src/vm/code_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,8 @@ impl CodeBlock {
| Opcode::PushClassField
| Opcode::SuperCallDerived
| Opcode::Await
| Opcode::PushNewTarget
| Opcode::NewTarget
| Opcode::ImportMeta
| Opcode::SuperCallPrepare
| Opcode::CallEvalSpread
| Opcode::CallSpread
Expand Down
3 changes: 2 additions & 1 deletion boa_engine/src/vm/flowgraph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ impl CodeBlock {
| Opcode::PushClassField
| Opcode::SuperCallDerived
| Opcode::Await
| Opcode::PushNewTarget
| Opcode::NewTarget
| Opcode::ImportMeta
| Opcode::CallEvalSpread
| Opcode::CallSpread
| Opcode::NewSpread
Expand Down
95 changes: 95 additions & 0 deletions boa_engine/src/vm/opcode/meta/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::unreachable;

use crate::{
module::ModuleKind,
vm::{opcode::Operation, ActiveRunnable, CompletionType},
Context, JsObject, JsResult, JsValue,
};

/// `NewTarget` implements the Opcode Operation for `Opcode::NewTarget`
///
/// Operation:
/// - Push the current new target to the stack.
#[derive(Debug, Clone, Copy)]
pub(crate) struct NewTarget;

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

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let new_target = if let Some(new_target) = context
.vm
.environments
.get_this_environment()
.as_function()
.and_then(|env| env.slots().new_target().cloned())
{
new_target.into()
} else {
JsValue::undefined()
};
context.vm.push(new_target);
Ok(CompletionType::Normal)
}
}

/// `ImportMeta` implements the Opcode Operation for `Opcode::ImportMeta`
///
/// Operation:
/// - Push the current `import.meta` to the stack
#[derive(Debug, Clone, Copy)]
pub(crate) struct ImportMeta;

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

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
jedel1043 marked this conversation as resolved.
Show resolved Hide resolved
// Meta Properties
//
// ImportMeta : import . meta
//
// https://tc39.es/ecma262/#sec-meta-properties

// 1. Let module be GetActiveScriptOrModule().

let Some(ActiveRunnable::Module(module)) = context.vm.active_runnable.clone() else {
unreachable!("2. Assert: module is a Source Text Module Record.");
};

let ModuleKind::SourceText(src) = module.kind() else {
unreachable!("2. Assert: module is a Source Text Module Record.");
};

// 3. Let importMeta be module.[[ImportMeta]].
// 4. If importMeta is empty, then
// 5. Else,
// a. Assert: importMeta is an Object.
let import_meta = src
.import_meta()
.borrow_mut()
.get_or_insert_with(|| {
// a. Set importMeta to OrdinaryObjectCreate(null).
let import_meta = JsObject::with_null_proto();

// b. Let importMetaValues be HostGetImportMetaProperties(module).
// c. For each Record { [[Key]], [[Value]] } p of importMetaValues, do
// i. Perform ! CreateDataPropertyOrThrow(importMeta, p.[[Key]], p.[[Value]]).
// d. Perform HostFinalizeImportMeta(importMeta, module).
context
.module_loader()
.init_import_meta(&import_meta, &module, context);

// e. Set module.[[ImportMeta]] to importMeta.
import_meta
})
.clone();

// b. Return importMeta.
// f. Return importMeta.
context.vm.push(import_meta);

Ok(CompletionType::Normal)
}
}
14 changes: 12 additions & 2 deletions boa_engine/src/vm/opcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mod generator;
mod get;
mod iteration;
mod jump;
mod meta;
mod new;
mod nop;
mod pop;
Expand Down Expand Up @@ -62,6 +63,8 @@ pub(crate) use iteration::*;
#[doc(inline)]
pub(crate) use jump::*;
#[doc(inline)]
pub(crate) use meta::*;
#[doc(inline)]
pub(crate) use new::*;
#[doc(inline)]
pub(crate) use nop::*;
Expand Down Expand Up @@ -1602,8 +1605,15 @@ generate_impl! {
///
/// Operands:
///
/// Stack: **=>** new_target
PushNewTarget,
/// Stack: **=>** `new.target`
NewTarget,

/// Push the current `import.meta` to the stack.
///
/// Operands:
///
/// Stack: **=>** `import.meta`
ImportMeta,

/// Pushes `true` to the stack if the top stack value is an object, or `false` otherwise.
///
Expand Down
2 changes: 0 additions & 2 deletions boa_engine/src/vm/opcode/push/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ pub(crate) mod array;
pub(crate) mod class;
pub(crate) mod environment;
pub(crate) mod literal;
pub(crate) mod new_target;
pub(crate) mod numbers;
pub(crate) mod object;

pub(crate) use array::*;
pub(crate) use class::*;
pub(crate) use environment::*;
pub(crate) use literal::*;
pub(crate) use new_target::*;
pub(crate) use numbers::*;
pub(crate) use object::*;

Expand Down
32 changes: 0 additions & 32 deletions boa_engine/src/vm/opcode/push/new_target.rs

This file was deleted.

3 changes: 2 additions & 1 deletion boa_interner/src/sym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,6 @@ static_syms! {
"__proto__",
"name",
"await",
("*default*", DEFAULT_EXPORT)
("*default*", DEFAULT_EXPORT),
"meta"
}
42 changes: 41 additions & 1 deletion boa_parser/src/parser/expression/left_hand_side/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,53 @@ where
cursor.set_goal(InputElement::RegExp);

let token = cursor.peek(0, interner).or_abrupt()?;
let position = token.span().start();
let mut lhs = match token.kind() {
TokenKind::Keyword((Keyword::New | Keyword::Super, true)) => {
TokenKind::Keyword((Keyword::New | Keyword::Super | Keyword::Import, true)) => {
return Err(Error::general(
"keyword must not contain escaped characters",
token.span().start(),
));
}
TokenKind::Keyword((Keyword::Import, false)) => {
cursor.advance(interner);

cursor.expect(
TokenKind::Punctuator(Punctuator::Dot),
"import.meta",
interner,
)?;

let token = cursor.next(interner).or_abrupt()?;

match token.kind() {
TokenKind::IdentifierName((Sym::META, ContainsEscapeSequence(ces))) => {
if *ces {
return Err(Error::general(
"`import.meta` cannot contain escaped characters",
token.span().start(),
));
}
}
_ => {
return Err(Error::expected(
["property `meta`".into()],
token.to_string(interner),
token.span(),
"import.meta",
));
}
}

if !cursor.module() {
return Err(Error::general(
"invalid `import.meta` expression outside a module",
position,
));
}

ast::Expression::ImportMeta
}
TokenKind::Keyword((Keyword::New, false)) => {
cursor.advance(interner);

Expand Down
Loading