mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 06:06:38 +00:00
JavaScript: a function made by the Function constructor does not bind the name anonymous
The constructor compiles its source as (function anonymous(...) {...}), and the
compiled function bound anonymous to itself as a named function expression does.
That binding shadowed a global variable of the same name. ES5.1 section 15.3.2.1
creates the function with the global environment as its scope and no name binding,
so the constructed function now leaves the name unbound.
This commit is contained in:
@@ -284,7 +284,7 @@ impl<'a> FnCompiler<'a> {
|
||||
|
||||
/// Compiles a nested function and returns its index in the function table.
|
||||
fn function(&mut self, fid: FunctionId) -> CResult<u32> {
|
||||
let code = compile_function(self.ast, fid, self.source, self.recursion)?;
|
||||
let code = compile_function(self.ast, fid, self.source, self.recursion, true)?;
|
||||
self.functions.push(code);
|
||||
Ok(u32::try_from(self.functions.len() - 1).unwrap_or(u32::MAX))
|
||||
}
|
||||
@@ -1208,8 +1208,9 @@ fn len_u32(n: usize) -> u32 {
|
||||
u32::try_from(n).unwrap_or(u32::MAX)
|
||||
}
|
||||
|
||||
/// Compiles function `fid` of `ast`.
|
||||
pub(crate) fn compile_function(ast: &Ast, fid: FunctionId, source: &Rc<Source>, recursion: u32) -> CResult<Rc<Code>> {
|
||||
/// Compiles function `fid` of `ast`. With `bind_name`, a function expression's name is bound
|
||||
/// inside it.
|
||||
pub(crate) fn compile_function(ast: &Ast, fid: FunctionId, source: &Rc<Source>, recursion: u32, bind_name: bool) -> CResult<Rc<Code>> {
|
||||
let Some(f) = ast.function(fid) else { return internal("function out of range") };
|
||||
let mut c = FnCompiler::new(ast, source, CodeKind::Function, f.strict, recursion);
|
||||
c.at(f.span.start);
|
||||
@@ -1221,7 +1222,7 @@ pub(crate) fn compile_function(ast: &Ast, fid: FunctionId, source: &Rc<Source>,
|
||||
c.emit(Op::Return);
|
||||
let name = f.name.as_ref().map(|n| key_of(&n.name));
|
||||
let meta = Meta {
|
||||
self_name: if f.kind == FunctionKind::Expression { name } else { None },
|
||||
self_name: if bind_name && f.kind == FunctionKind::Expression { name } else { None },
|
||||
params: f.params.iter().map(|p| key_of(&p.name)).collect(),
|
||||
span: (f.span.start, f.span.end),
|
||||
};
|
||||
@@ -1239,11 +1240,12 @@ pub(crate) fn compile_program(program: &crate::Program, source: &Rc<Source>, kin
|
||||
}
|
||||
|
||||
/// The function of a `Function` constructor's source, `(function anonymous(…) {…})`: the program
|
||||
/// must be that one parenthesized function expression and nothing else.
|
||||
/// must be that one parenthesized function expression and nothing else. The name `anonymous` is
|
||||
/// not bound inside the function (section 15.3.2.1).
|
||||
pub(crate) fn compile_constructed_function(program: &crate::Program, source: &Rc<Source>) -> CResult<Option<Rc<Code>>> {
|
||||
let [stmt] = program.body.as_slice() else { return Ok(None) };
|
||||
let Some(Stmt::Expr(e)) = program.ast.stmt(*stmt) else { return Ok(None) };
|
||||
let Some(Expr::Paren(inner)) = program.ast.expr(*e) else { return Ok(None) };
|
||||
let Some(Expr::Function(fid)) = program.ast.expr(*inner) else { return Ok(None) };
|
||||
compile_function(&program.ast, *fid, source, 0).map(Some)
|
||||
compile_function(&program.ast, *fid, source, 0, false).map(Some)
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ fn with_eval_and_new_function() {
|
||||
assert_eq!(num("eval('1; 2; if (false) {}')"), 2.0);
|
||||
assert_eq!(num("new Function('a', 'b', 'return a * b;')(6, 7)"), 42.0);
|
||||
assert_eq!(num("Function('return 3')()"), 3.0);
|
||||
assert_eq!(num("var anonymous = 42; Function('return anonymous')()"), 42.0);
|
||||
let e = fail("new Function('a){}; (function(', 'return 1')");
|
||||
assert!(e.message.starts_with("SyntaxError"), "{}", e.message);
|
||||
let e = fail("new Function('}); (function() {')");
|
||||
|
||||
Reference in New Issue
Block a user