///|
/// Spread an iterable value into an array of values using the iterator protocol
pub fn Interpreter::spread_iterable(
self : Interpreter,
val : Value,
loc : @token.Loc,
) -> Array[Value] raise Error {
let result : Array[Value] = []
// Try to get the iterator via Symbol.iterator protocol
let iterator_sym = self.realm_state.well_known_symbols.iterator
let iterator_method = self.get_computed_property(
val,
Symbol(iterator_sym),
loc,
)
// Get the iterator object
let iterator = match iterator_method {
Object(data) =>
match data.callable {
Some(_) => self.call_value(iterator_method, val, [], loc)
None =>
raise @errors.TypeError(
message=type_of(val) +
" is not iterable (Symbol.iterator is not a function)",
)
}
Undefined =>
// No iterator method — fall back for arrays/strings which are built-in iterables
match val {
Array(arr) => {
for el in arr.elements {
result.push(el)
}
return result
}
String_(s) => {
let chars = s.to_array()
for c in chars {
let buf = StringBuilder::new()
buf.write_char(c)
result.push(String_(buf.to_string()))
}
return result
}
_ => raise @errors.TypeError(message=type_of(val) + " is not iterable")
}
_ => raise @errors.TypeError(message=type_of(val) + " is not iterable")
}
let next_method = self.get_iterator_next_method(iterator, loc)
// Iterate using the iterator protocol
while true {
match self.iterator_step_value(iterator, next_method, loc) {
None => break
Some(value) => result.push(value)
}
}
result
}
///|
/// Raise SyntaxError for Break/Continue signals escaping a function boundary.
/// Used as a shared invariant at all function-body and module execution sites.
fn raise_if_break_continue(sig : Signal) -> Unit raise Error {
match sig {
BreakSignal(_, _) =>
raise @errors.SyntaxError(message="break statement outside of loop")
ContinueSignal(_, _) =>
raise @errors.SyntaxError(message="continue statement outside of loop")
_ => ()
}
}
///|
fn Interpreter::eval_args_with_spread(
self : Interpreter,
ctx : ExecContext,
arg_exprs : Array[@ast.Expr],
env : Environment,
) -> Array[Value] raise Error {
let args : Array[Value] = []
for a in arg_exprs {
match a {
SpreadExpr(inner, loc) => {
let val = self.eval_expr(ctx, inner, env)
// Use iterator protocol for spreading
let spread_vals = self.spread_iterable(val, loc)
for v in spread_vals {
args.push(v)
}
}
_ => args.push(self.eval_expr(ctx, a, env))
}
}
args
}
///|
/// Check if a value is the global eval function
fn is_eval_function(val : Value) -> Bool {
match val {
Object(obj_data) =>
match obj_data.callable {
Some(NonConstructableCallable("eval", _)) => true
_ => false
}
_ => false
}
}
///|
pub fn load_direct_eval_callee(env : Environment) -> Value raise Error {
env.get("eval") catch {
_ => raise @errors.ReferenceError(message="eval is not defined")
}
}
///|
pub fn Interpreter::call_direct_eval_or_shadowed(
self : Interpreter,
callee : Value,
args : Array[Value],
env : Environment,
loc : @token.Loc,
caller_strict~ : Bool,
) -> Value raise Error {
if is_eval_function(callee) {
if args.length() == 0 {
return Undefined
}
match args[0] {
String_(code) => self.perform_eval(code, env, true, caller_strict~)
other => other
}
} else {
self.call_value(callee, Undefined, args, loc)
}
}
///|
/// Strip consecutive Grouping nodes from an expression.
/// Per ES spec, grouping parentheses do not change the Reference type,
/// so ((eval))("code") is still a direct eval call.
pub fn unwrap_grouping(expr : @ast.Expr) -> @ast.Expr {
let mut current = expr
for ;; {
match current {
Grouping(inner, _) => current = inner
_ => return current
}
}
}
///|
fn collect_param_default_eval_var_conflicts(
params : Array[@ast.Param],
rest_param : String?,
implicit_arguments? : Bool = false,
) -> @set.Set[String] {
let conflicts = @set.Set::default()
if implicit_arguments {
conflicts.add("arguments")
}
for param in params {
match param.pattern {
Some(pattern) =>
for name in @static_semantics.bound_names(pattern) {
conflicts.add(name)
}
None => if !param.is_rest_pattern { conflicts.add(param.name) }
}
}
match rest_param {
Some(name) => conflicts.add(name)
None => ()
}
conflicts
}
///|
/// Shared core of UserFuncExt and ArrowFuncExt call paths: bind
/// parameters (with defaults, destructuring, rest), optionally split
/// the body env per §10.2.11, hoist body declarations, execute the
/// body, and reduce the completion to a plain Value.
///
/// Caller is responsible for everything that differs between named
/// FunctionExpression calls and arrow calls:
/// - `param_env`'s parent (the named-FE self-name wrapper, or
/// `data.closure` directly for arrows)
/// - Strict-mode duplicate / reserved-name validation
/// - `this` / `` / `arguments` installs (arrows skip them
/// entirely — ThisMode=lexical per §10.2.11 step 18).
///
/// Per §10.2.11 step 26 the split is gated on
/// `has_parameter_expressions(data.params)` — plain-rest and
/// plain-destructure shapes stay single-env so a body-level
/// `var ` reuses the param binding.
fn Interpreter::bind_ext_params_and_exec_body_signal(
self : Interpreter,
data : FuncDataExt,
args : Array[Value],
param_env : Environment,
func_ctx : ExecContext,
is_arrow? : Bool = false,
) -> Signal raise Error {
// §19.2.1.3 (#A.6): save on entry, reset to false so nested function
// invocations in param defaults (IIFE, arrow, recursive call) do not
// inherit this frame's state. Restored on both the normal and raise
// paths below so any abnormal completion during default evaluation
// or body execution leaves the flag clean for outer frames.
let saved_in_default = self.in_nonarrow_param_default_eval
let saved_param_default_conflicts = self.param_default_eval_var_conflicts
self.in_nonarrow_param_default_eval = false
self.param_default_eval_var_conflicts = None
let param_default_conflicts = collect_param_default_eval_var_conflicts(
data.params,
data.rest_param,
implicit_arguments=!is_arrow,
)
let result : Signal = try {
// §10.2.11 step 21: pre-declare all param BoundNames as TDZ so that
// self- and forward-referencing defaults throw ReferenceError.
let mut has_rest_pattern_param = false
for p in data.params {
if p.is_rest_pattern {
has_rest_pattern_param = true
// Declare bound names from the rest destructuring pattern (...[a] → a),
// but NOT the synthetic "$rest" name stored in rest_param.
match p.pattern {
Some(pat) =>
for name in @static_semantics.bound_names(pat) {
param_env.def_param_tdz(name)
}
None => ()
}
continue
}
match p.pattern {
Some(pat) =>
// Destructuring param: pre-declare all bound names (e.g. {y} → y).
for name in @static_semantics.bound_names(pat) {
param_env.def_param_tdz(name)
}
None => param_env.def_param_tdz(p.name)
}
}
// Simple named rest (e.g. ...rest): pre-declare the user name.
// Destructuring rest (...[a]): bound names declared above via BoundNames.
if !has_rest_pattern_param {
match data.rest_param {
Some(rp) => param_env.def_param_tdz(rp)
None => ()
}
}
// Bind params with defaults
let mut effective_param_count = 0
for i = 0; i < data.params.length(); i = i + 1 {
let param = data.params[i]
// Skip destructuring-rest params; they are bound from the rest array below.
if param.is_rest_pattern {
continue
}
let val : Value = if effective_param_count < args.length() &&
!(args[effective_param_count] is Undefined) {
args[effective_param_count]
} else {
match param.default_val {
Some(default_expr) => {
// §19.2.1.3 gate: active only around default evaluation.
self.in_nonarrow_param_default_eval = true
self.param_default_eval_var_conflicts = Some(
param_default_conflicts,
)
let v = match param.pattern {
None =>
self.eval_named_expr(
func_ctx,
default_expr,
param_env,
param.name,
)
Some(_) => self.eval_expr(func_ctx, default_expr, param_env)
}
self.in_nonarrow_param_default_eval = false
self.param_default_eval_var_conflicts = None
v
}
None =>
if effective_param_count < args.length() {
args[effective_param_count]
} else {
Undefined
}
}
}
// Destructure pattern params without exposing their synthetic names.
match param.pattern {
Some(pat) =>
self.bind_pattern(pat, val, param_env, LetBinding, ctx=func_ctx)
None => param_env.initialize(param.name, val)
}
effective_param_count = effective_param_count + 1
}
// Rest param
match data.rest_param {
Some(rest_name) => {
let rest_elements : Array[Value] = []
for i in effective_param_count..
self.bind_pattern(
pat,
rest_val,
param_env,
LetBinding,
ctx=func_ctx,
)
None => ()
}
bound_rest_pattern = true
break
}
}
if !bound_rest_pattern {
param_env.initialize(rest_name, rest_val)
}
}
None => ()
}
// Split the body env iff HasParameterExpressions; otherwise body
// decls hoist directly onto param_env (same semantics as non-Ext).
let split_scope = has_parameter_expressions(data.params)
let body_env = if split_scope {
let be = Environment::new(parent=Some(param_env))
be.is_var_scope = true
be
} else {
param_env
}
let param_source : Environment? = if split_scope {
Some(param_env)
} else {
None
}
self.hoist_declarations(
data.body,
body_env,
strict=data.strict,
param_source~,
)
hoist_block_tdz(data.body, body_env)
let sig = self.exec_stmts(func_ctx, data.body, body_env)
raise_if_break_continue(sig)
sig
} catch {
e => {
self.in_nonarrow_param_default_eval = saved_in_default
self.param_default_eval_var_conflicts = saved_param_default_conflicts
raise e
}
}
self.in_nonarrow_param_default_eval = saved_in_default
self.param_default_eval_var_conflicts = saved_param_default_conflicts
result
}
///|
/// Call-mode wrapper applying ES §10.2.1 [[Call]] return rule.
fn Interpreter::bind_ext_params_and_exec_body(
self : Interpreter,
data : FuncDataExt,
args : Array[Value],
param_env : Environment,
func_ctx : ExecContext,
is_arrow? : Bool = false,
) -> Value raise Error {
match
self.bind_ext_params_and_exec_body_signal(
data,
args,
param_env,
func_ctx,
is_arrow~,
) {
ReturnSignal(v) => v
_ => Undefined
}
}
///|
/// Resolve PerformEval's super permissions from the current this environment.
/// Ordinary functions install an explicit boundary; arrows do not, so only
/// arrows inherit the surrounding method or constructor's lexical super.
fn current_eval_super_context(env : Environment) -> (Bool, Bool) {
match env.bindings.get("[[EvalMethodContext]]") {
Some(binding) =>
match binding.value {
Bool(is_method) => (is_method, false)
_ => (false, false)
}
None => {
if env.bindings.contains("[[ActiveClassFunction]]") {
return (true, true)
}
if env.bindings.contains("[[SuperPrototype]]") ||
env.bindings.contains("[[InClassFieldInitializer]]") {
return (true, false)
}
match env.parent {
Some(parent) => current_eval_super_context(parent)
None => (false, false)
}
}
}
}
///|
/// Execute eval code in a given environment.
/// If `direct` is true, executes in the caller's environment (direct eval).
/// If `direct` is false, executes in the global environment (indirect eval).
/// Handles strict mode isolation and var leaking per ES spec.
///
/// Per ES spec (18.2.1.1 PerformEval):
/// - Strict eval: all declarations (var, let, const, function) are isolated in a new scope
/// - Non-strict direct eval: var/function declarations leak to caller's variable environment,
/// but let/const are isolated in a new eval lexical scope
/// - Non-strict indirect eval: var/function declarations leak to global scope,
/// but let/const are isolated in a new eval lexical scope
pub fn Interpreter::perform_eval(
self : Interpreter,
code : String,
caller_env : Environment,
direct : Bool,
caller_strict? : Bool = false,
) -> Value raise Error {
// Parse the code — syntax errors propagate as SyntaxError
let prog = @parser.parse(code)
let stmts = prog.stmts
if stmts.length() == 0 {
return Undefined
}
// Determine if eval code is strict.
// Direct eval inherits strictness from the caller OR its own "use strict" directive.
// Indirect eval is strict ONLY if the eval code itself has "use strict" — it does
// NOT inherit the caller's strict mode (it runs as a fresh global script).
let eval_strict = if direct {
caller_strict || @static_semantics.has_use_strict(stmts)
} else {
@static_semantics.has_use_strict(stmts)
}
// Apply static block-scoped redeclaration checks before declaration instantiation.
self.validate_block_early_errors(stmts, eval_strict)
// ES262 §19.2.1.1 PerformEval steps 8–14: reject the eval source when it
// references super/new.target/arguments outside the surrounding context's
// permitted form. Indirect eval inherits no surrounding context, so all
// four flags are false and any of the predicates triggers the error.
// Direct eval looks at the caller's env chain via existing markers.
let in_function = if direct { caller_env.has("") } else { false }
let (in_method, in_derived_constructor) = if direct {
current_eval_super_context(caller_env)
} else {
(false, false)
}
let in_class_field_initializer = if direct {
caller_env.has("[[InClassFieldInitializer]]")
} else {
false
}
let scan = scan_eval_contains(stmts)
if scan.super_call && !in_derived_constructor {
raise @errors.SyntaxError(
message="'super' call is not allowed here — eval source contains super() outside a derived class constructor",
)
}
if scan.super_property && !in_method {
raise @errors.SyntaxError(
message="'super' property access is not allowed here — eval source contains super.x outside a method",
)
}
if scan.new_target && !in_function {
raise @errors.SyntaxError(
message="'new.target' is not allowed outside of a function — eval source contains new.target at the top level",
)
}
if scan.arguments_ref && in_class_field_initializer {
raise @errors.SyntaxError(
message="'arguments' is not allowed in a class field initializer — eval source references arguments",
)
}
// Determine the variable environment (where var/function declarations leak to).
// For direct eval, walk up from the caller's env to find the function/global scope
// (the nearest is_var_scope environment). This ensures that eval("var x = 1")
// inside a block correctly hoists to the enclosing function scope, not the block.
let var_env : Environment = if direct {
caller_env.find_var_env()
} else {
self.global
}
// Always create a new scope for eval execution.
// This isolates let/const declarations from the caller.
// For strict eval, it also isolates var/function declarations.
// For direct eval, parent to caller_env so block-scoped bindings are visible:
// { let x = 1; eval("x") } must see x.
// For indirect eval, parent to global scope since it runs as a fresh script.
let exec_parent = if direct { caller_env } else { self.global }
let exec_env = Environment::new(parent=Some(exec_parent))
let eval_ctx : ExecContext = { strict: eval_strict, current_generator: None }
if eval_strict {
// Strict eval: all declarations stay in the eval scope (isolated)
self.hoist_declarations(stmts, exec_env, strict=eval_strict)
hoist_block_tdz(stmts, exec_env)
} else {
// EvalDeclarationInstantiation steps 5.a and 5.d:
// Check for var/lexical conflicts before hoisting.
let var_names = collect_eval_var_names(stmts)
// §19.2.1.3 gate (#A.6): a direct eval in a parameter default cannot
// var-declare names that belong to that call's parameter scope. The
// precomputed map includes later formals that are not bound yet, and the
// implicit non-arrow `arguments` binding.
if direct && self.in_nonarrow_param_default_eval {
for name in var_names {
let conflicts = match self.param_default_eval_var_conflicts {
Some(conflict_names) => conflict_names.contains(name)
None =>
match var_env.bindings.get(name) {
Some(binding) => binding.is_parameter || name == "arguments"
None => false
}
}
if conflicts {
raise @errors.SyntaxError(
message="Identifier '\{name}' has already been declared",
)
}
}
}
// Step 5.a: If var_env is the global Environment Record, check that
// var names don't conflict with global lexical (let/const) declarations.
if var_env.parent is None {
for name in var_names {
match var_env.bindings.get(name) {
Some(b) =>
if b.kind == LetBinding || b.kind == ConstBinding {
raise @errors.SyntaxError(
message="Identifier '\{name}' has already been declared",
)
}
_ => ()
}
}
}
// Step 5.d (ES §19.2.1.3): walk from the fresh eval lex env (NOT the
// caller env) up to varEnv exclusive. For indirect eval this is trivially
// the new eval env rooted at global — the walk runs at most once over
// exec_env, which holds only the internal reconcile marker. For direct
// eval the walk crosses caller-side block scopes, catching `let`/`const`
// conflicts with the incoming var names.
//
// Per §B.3.4, catch environments are transparent for this check. Our env
// model does not distinguish catch envs from other block envs, so that
// edge case remains a known gap (see agent-todo).
exec_env.set_marker(EVAL_FUNCTION_RECONCILE_MARKER)
{
let mut this_lex = exec_env
while !this_lex.is_var_scope {
for name in var_names {
if this_lex.bindings.contains(name) {
raise @errors.SyntaxError(
message="Identifier '\{name}' has already been declared",
)
}
}
match this_lex.parent {
Some(parent) => this_lex = parent
None => break
}
}
}
for name in var_names {
if !var_env.bindings.contains(name) {
var_env.set_marker(eval_deletable_var_marker(name))
}
}
// Non-strict eval: hoist var/function to the variable environment (leak),
// then hoist let/const TDZ markers to the eval scope (isolated).
// `suppress_annex_b_candidates` defers the Annex B walk so we can slot
// it between var hoisting and TDZ setup, matching §19.2.1.3's ordering.
self.hoist_declarations(stmts, var_env, suppress_annex_b_candidates=true)
hoist_block_tdz(stmts, exec_env)
// Annex B §B.3.2.3: promote eligible block-level FunctionDeclarations
// to var bindings on `var_env`, skipping names that would clash with an
// outer lex declaration. Same walker as §B.3.2.1 used for function
// bodies in `hoist_declarations`.
if self.annex_b {
let top_lex = collect_stmts_lex_names(stmts)
hoist_eval_annex_b_candidates(self, stmts, var_env, top_lex)
}
}
// Execute statements and collect result
let mut last : Value = Undefined
for stmt in stmts {
match self.exec_stmt(eval_ctx, stmt, exec_env) {
Normal(v) =>
// Per spec, declarations have empty completion values
if !is_declaration_stmt(stmt) {
last = v
}
ReturnSignal(_) =>
raise @errors.SyntaxError(
message="return statement outside of function",
)
sig => raise_if_break_continue(sig)
}
}
last
}
///|
fn Interpreter::eval_call(
self : Interpreter,
ctx : ExecContext,
callee_expr : @ast.Expr,
arg_exprs : Array[@ast.Expr],
env : Environment,
loc : @token.Loc,
) -> Value raise Error {
// Detect direct eval: eval(...), (eval)(...), ((eval))(...) etc.
// Per ES spec, grouping parentheses do not change the Reference type,
// so any nesting of parens around eval is still a direct eval call.
match unwrap_grouping(callee_expr) {
Ident("eval", _) => {
// Check if this identifier actually resolves to the global eval function
let callee = env.get("eval") catch {
_ =>
// eval is not defined - throw ReferenceError
raise @errors.ReferenceError(message="eval is not defined")
}
if is_eval_function(callee) {
// Direct eval: parse and execute in caller's environment
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
if args.length() == 0 {
return Undefined
}
match args[0] {
String_(code) =>
return self.perform_eval(code, env, true, caller_strict=ctx.strict)
other => return other // non-string argument returns as-is
}
}
// eval has been shadowed by a non-eval binding — call normally
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(callee, Undefined, args, loc)
}
_ => {
// Per ES spec (13.3.6.1), evaluate callee/receiver first, then arguments.
// Grouping ends the optional-chain boundary: (a?.b)() is a plain call on
// the chain result, not a?.b(). Detect it once so the Optional arms can
// decide whether a short-circuit returns Undefined or reaches call_value.
let was_grouped = callee_expr is Grouping(_)
let unwrapped = unwrap_grouping(callee_expr)
match unwrapped {
OptionalCall(inner_callee, inner_args, inner_loc) => {
// was_grouped=false: a?.b?.()() — outer call short-circuits too.
// was_grouped=true: (a?.b?.())() — grouping ended chain; outer always
// runs; if inner short-circuited to Undefined, call_value TypeError.
let (value, short_circuited) = self.eval_chain_expr(
ctx,
OptionalCall(inner_callee, inner_args, inner_loc),
env,
)
if short_circuited && !was_grouped {
Undefined
} else {
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(value, Undefined, args, loc)
}
}
OptionalMember(obj_expr, prop, mloc) => {
// was_grouped=false: a?.b() — short-circuit returns Undefined.
// was_grouped=true: (a?.b)() — pass Undefined to call_value (TypeError).
let obj = self.eval_expr(ctx, obj_expr, env)
match obj {
Null | Undefined =>
if was_grouped {
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(Undefined, obj, args, loc)
} else {
Undefined
}
_ => {
let func_val = self.get_property(obj, prop, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
}
}
OptionalComputedMember(obj_expr, key_expr, mloc) => {
let obj = self.eval_expr(ctx, obj_expr, env)
match obj {
Null | Undefined =>
if was_grouped {
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(Undefined, obj, args, loc)
} else {
Undefined
}
_ => {
let key = self.eval_expr(ctx, key_expr, env)
let func_val = self.get_computed_property(obj, key, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
}
}
// Parser emits OptionalCall, not plain Call, inside optional chains;
// keep these arms for hand-built ASTs and future parser changes.
ChainMember(obj_expr, prop, mloc) => {
let (obj, short_circuited) = self.eval_chain_expr(ctx, obj_expr, env)
if short_circuited {
Undefined
} else {
let func_val = self.get_property(obj, prop, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
}
ChainComputedMember(obj_expr, key_expr, mloc) => {
let (obj, short_circuited) = self.eval_chain_expr(ctx, obj_expr, env)
if short_circuited {
Undefined
} else {
let key = self.eval_expr(ctx, key_expr, env)
let func_val = self.get_computed_property(obj, key, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
}
// For member and super references, grouping preserves the Reference,
// so ((a.b))() correctly calls with this=a.
Member(obj_expr, prop, mloc) =>
match obj_expr {
Ident("console", _) => {
let callee = self.eval_member(ctx, obj_expr, prop, env, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(callee, Undefined, args, loc)
}
_ => {
let obj = self.eval_expr(ctx, obj_expr, env)
let func_val = self.get_property(obj, prop, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
}
ComputedMember(obj_expr, key_expr, mloc) => {
let obj = self.eval_expr(ctx, obj_expr, env)
let key = self.eval_expr(ctx, key_expr, env)
let func_val = self.get_computed_property(obj, key, mloc)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
PrivateMember(obj_expr, name, _) => {
let obj = self.eval_expr(ctx, obj_expr, env)
let func_val = get_private_member(obj, name, env)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, obj, args, loc)
}
SuperMember(prop, sloc) => {
let (this_val, func_val) = self.eval_super_property_call_reference(
env, prop, sloc,
)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, this_val, args, loc)
}
SuperComputedMember(key_expr, sloc) => {
let _ = eval_this_value(env)
let key = self.eval_expr(ctx, key_expr, env)
let (this_val, func_val) = self.eval_super_computed_call_reference(
env, key, sloc,
)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(func_val, this_val, args, loc)
}
_ => {
let callee = self.eval_expr(ctx, callee_expr, env)
let args = self.eval_args_with_spread(ctx, arg_exprs, env)
self.call_value(callee, Undefined, args, loc)
}
}
}
}
}
///|
priv enum CallForwardingDecision {
Forward(Value, Value, Array[Value])
PrepareApply(Value, Value, Value)
NotForwarded
}
///|
// Engine-private provenance for exact runtime-created Arguments objects.
let runtime_arguments_host_slot : HostSlotKey = HostSlotKey::reserve()
///|
fn call_forwarding_args(args : Array[Value]) -> Array[Value] {
let call_args : Array[Value] = []
for i = 1; i < args.length(); i = i + 1 {
call_args.push(args[i])
}
call_args
}
///|
fn call_forwarding_decision(
callee : Value,
this_val : Value,
args : Array[Value],
) -> CallForwardingDecision {
match callee {
Object(data) =>
match data.callable {
// `FuncCallMethod(Undefined)` is the runtime-owned identity for the
// exact Function.prototype.call intrinsic. Fallback `.call` wrappers
// capture callable Objects, so their target can never be Undefined.
Some(FuncCallMethod(Undefined)) => {
let call_this = if args.length() > 0 { args[0] } else { Undefined }
Forward(this_val, call_this, call_forwarding_args(args))
}
Some(FuncCallMethod(target)) => {
let call_this = if args.length() > 0 { args[0] } else { Undefined }
Forward(target, call_this, call_forwarding_args(args))
}
// `FuncApplyMethod(Undefined)` is the runtime-owned identity for the
// exact Function.prototype.apply intrinsic. Its target is the
// current receiver, just as the exact Function.prototype.call
// intrinsic above forwards its current receiver.
Some(FuncApplyMethod(Undefined)) => {
let call_this = if args.length() > 0 { args[0] } else { Undefined }
let arg_array = if args.length() > 1 { args[1] } else { Undefined }
PrepareApply(this_val, call_this, arg_array)
}
Some(FuncApplyMethod(target)) => {
let call_this = if args.length() > 0 { args[0] } else { Undefined }
let arg_array = if args.length() > 1 { args[1] } else { Undefined }
PrepareApply(target, call_this, arg_array)
}
_ => NotForwarded
}
_ => NotForwarded
}
}
///|
priv enum ApplyArgument {
Direct(Value)
Mapped(PropDescriptor)
}
///|
fn dense_array_apply_shape(data : ArrayData) -> Array[ApplyArgument]? {
let len = array_logical_length(data)
// A sealed dense list has no length override and therefore cannot expose
// sparse slots or an arbitrarily large logical length through this path.
guard len == data.elements.length().to_int64() else { return None }
let mut i = 0L
while i < len {
guard array_index_lookup_result64(data, i) is Present(_) else {
return None
}
i += 1L
}
let result : Array[ApplyArgument] = []
i = 0L
while i < len {
match array_index_lookup_result64(data, i) {
Present(value) => result.push(Direct(value))
_ => return None
}
i += 1L
}
Some(result)
}
///|
fn mapped_arguments_accessor(desc : PropDescriptor) -> Bool {
guard desc.is_accessor else { return false }
match (desc.getter, desc.setter) {
(
Some(
Object({ callable: Some(NativeCallable("[[MappedArgGetter]]", _)), .. })
),
Some(
Object({ callable: Some(NativeCallable("[[MappedArgSetter]]", _)), .. })
),
) => true
_ => false
}
}
///|
fn mapped_arguments_value(desc : PropDescriptor) -> Value raise Error {
match desc.getter {
Some(
Object(
{ callable: Some(NativeCallable("[[MappedArgGetter]]", getter)), .. }
)
) => getter([])
_ => Undefined
}
}
///|
fn exact_arguments_apply_shape(data : ObjectData) -> Array[ApplyArgument]? {
// A host slot is the non-forgeable provenance marker installed only by
// make_arguments_object. The class name is retained as a defensive shape
// check, but is never used as provenance on its own.
guard data.class_name == "Arguments" &&
has_host_slot(data, runtime_arguments_host_slot) else {
return None
}
let length_value = data.bag.properties.get("length")
let length_desc = data.bag.descriptors.get("length")
let n = match length_value {
Some(Number(n)) => n
_ => return None
}
let desc = match length_desc {
Some(desc) => desc
_ => return None
}
guard !desc.is_accessor &&
n >= 0.0 &&
n == n.floor() &&
n <= JS_MAX_SAFE_INTEGER_DOUBLE else {
return None
}
let length = n.to_int64()
guard length <= data.bag.properties.length().to_int64() else { return None }
let mut i = 0L
while i < length {
let key = i.to_string()
match (data.bag.descriptors.get(key), data.bag.properties.get(key)) {
(Some(desc), Some(_)) if !desc.is_accessor => ()
(Some(desc), Some(_)) if mapped_arguments_accessor(desc) => ()
_ => return None
}
i += 1L
}
let result : Array[ApplyArgument] = []
i = 0L
while i < length {
let key = i.to_string()
match (data.bag.descriptors.get(key), data.bag.properties.get(key)) {
(Some(desc), Some(value)) if !desc.is_accessor =>
result.push(Direct(value))
(Some(desc), Some(_)) if mapped_arguments_accessor(desc) =>
result.push(Mapped(desc))
_ => return None
}
i += 1L
}
Some(result)
}
///|
fn Interpreter::materialize_apply_shape(
self : Interpreter,
shape : Array[ApplyArgument],
) -> Array[Value] raise Error {
let _ = self
let result : Array[Value] = []
for item in shape {
match item {
Direct(value) => result.push(value)
Mapped(desc) => result.push(mapped_arguments_value(desc))
}
}
result
}
///|
fn prepare_apply_shape(
target : Value,
arg_array : Value,
) -> Array[ApplyArgument]? raise Error {
// §20.2.3.1 checks IsCallable before touching argArray. This check also
// applies when the exact intrinsic is reached through Function#call.
guard is_callable(target) else {
raise @errors.TypeError(message="is not a function")
}
match arg_array {
Undefined | Null => Some([])
Array(data) => dense_array_apply_shape(data)
Object(data) => exact_arguments_apply_shape(data)
_ => None
}
}
///|
fn Interpreter::resolve_call_forwarding(
self : Interpreter,
callee : Value,
this_val : Value,
args : Array[Value],
) -> (Value, Value, Array[Value]) raise Error {
let mut current_callee = callee
let mut current_this_val = this_val
let mut current_args = args
for ;; {
match
call_forwarding_decision(current_callee, current_this_val, current_args) {
Forward(next_callee, next_this_val, next_args) => {
current_callee = next_callee
current_this_val = next_this_val
current_args = next_args
}
PrepareApply(target, call_this, arg_array) =>
match prepare_apply_shape(target, arg_array) {
Some(shape) => {
let next_args = self.materialize_apply_shape(shape)
current_callee = target
current_this_val = call_this
current_args = next_args
}
// Shapes that are observable or unsupported by the sealed-list
// classifier retain the legacy FuncApplyMethod call below.
None => break
}
NotForwarded => break
}
}
(current_callee, current_this_val, current_args)
}
///|
// Enter an already-proven ordinary UserFunc as a tree executor root when the
// legacy evaluator reaches it. The surrounding legacy caller stays outside
// the tree frame; only the admitted child and its callback-free graph run in
// the executor-neutral coordinator.
fn Interpreter::try_tree_executor_admitted_call(
self : Interpreter,
callee : Value,
this_value : Value,
args : Array[Value],
loc : @token.Loc,
) -> Value? raise Error {
guard callee is Object(object_data) else { return None }
guard object_data.callable is Some(UserFunc(data)) else { return None }
let admission = match tree_executor_callable_admission(callee, data, args) {
Some(admission) => admission
None => return None
}
Some(
executor_call_completion(
admission.executable.kind,
self.run_admitted_executor_call_root(
admission.executable,
ExecutorCallRequest(callee~, this_value~, args~, loc~),
admission.cursor,
),
),
)
}
///|
pub fn Interpreter::call_value(
self : Interpreter,
callee : Value,
this_val : Value,
args : Array[Value],
loc : @token.Loc,
) -> Value raise Error {
let (current_callee, current_this_val, current_args) = self.resolve_call_forwarding(
callee, this_val, args,
)
match
self.preflight_changing_receiver_call(
current_callee, current_this_val, current_args, loc,
) {
Some(preflight) => {
let registry = self.seal_changing_receiver_registry(
preflight, current_callee, current_this_val, current_args, loc,
)
let completion = self.run_activation_dispatch_changing_receiver_call(
DispatchCallRequest(
callee=current_callee,
this_value=current_this_val,
args=current_args,
loc~,
),
registry,
)
return activation_dispatch_root_value(completion) catch {
ExecutionControlError(StackDepthLimit) as depth_error => {
let translated = JsException(
js_error_to_value_with_env(depth_error, Some(self.global)),
)
remap_observed_source_failure(
self.realm_state,
depth_error,
translated,
)
raise translated
}
error => raise error
}
}
None => ()
}
match
self.preflight_direct_numeric_recursion_call(
current_callee, current_this_val, current_args, loc,
) {
Some(preflight) => {
let registry = self.seal_direct_numeric_recursion_registry(
preflight, current_callee, current_this_val, current_args, loc,
)
let completion = self.run_activation_dispatch_numeric_call(
DispatchCallRequest(
callee=current_callee,
this_value=current_this_val,
args=current_args,
loc~,
),
registry,
)
return activation_dispatch_root_value(completion) catch {
ExecutionControlError(StackDepthLimit) as depth_error => {
let translated = JsException(
js_error_to_value_with_env(depth_error, Some(self.global)),
)
remap_observed_source_failure(
self.realm_state,
depth_error,
translated,
)
raise translated
}
error => raise error
}
}
None => ()
}
// Fast path: skip both realm-proto wrapper layers when every active-override
// slot is None and every callee stamped proto matches the main realm's (or is
// absent). Checks every packed realm slot so it is safe even when
// stamp_function_realm_with sets slots independently or when a non-function
// override is active.
if realm_fast_path_allowed(current_callee, self.realm_state) {
self.call_value_impl(current_callee, current_this_val, current_args, loc)
} else {
self.with_active_value(fn() raise {
with_active_callee_realm_value(self.realm_state, current_callee, fn() raise {
self.call_value_impl(
current_callee, current_this_val, current_args, loc,
)
})
})
}
}
///|
fn box_primitive_call_this(value : Value, realm_state : RealmState) -> Value {
match value {
String_(s) => {
let utf16_units = string_to_utf16(s)
let properties : Map[String, Value] = {
"length": Number(utf16_units.length().to_double()),
}
let descriptors : Map[String, PropDescriptor] = {
"length": {
writable: false,
enumerable: false,
configurable: false,
getter: None,
setter: None,
is_accessor: false,
},
}
for i = 0; i < utf16_units.length(); i = i + 1 {
let key = i.to_string()
properties[key] = String_(
String::make(1, utf16_units[i].unsafe_to_char()),
)
descriptors[key] = {
writable: false,
enumerable: true,
configurable: false,
getter: None,
setter: None,
is_accessor: false,
}
}
Object({
bag: {
properties,
symbol_properties: Map([]),
descriptors,
symbol_descriptors: Map([]),
internal_slots: Map::from_array([(StringData, String_(s))]),
host_slots: Map([]),
},
prototype: get_string_proto(realm_state=Some(realm_state)),
callable: None,
class_name: "String",
extensible: true,
arraybuffer_state: None,
})
}
Number(n) =>
Object({
bag: {
properties: Map([]),
symbol_properties: Map([]),
descriptors: Map([]),
symbol_descriptors: Map([]),
internal_slots: Map::from_array([(NumberData, Number(n))]),
host_slots: Map([]),
},
prototype: get_number_proto(realm_state=Some(realm_state)),
callable: None,
class_name: "Number",
extensible: true,
arraybuffer_state: None,
})
Bool(b) =>
Object({
bag: {
properties: Map([]),
symbol_properties: Map([]),
descriptors: Map([]),
symbol_descriptors: Map([]),
internal_slots: Map::from_array([(BooleanData, Bool(b))]),
host_slots: Map([]),
},
prototype: get_boolean_proto(realm_state=Some(realm_state)),
callable: None,
class_name: "Boolean",
extensible: true,
arraybuffer_state: None,
})
Symbol(sym) =>
Object({
bag: {
properties: Map([]),
symbol_properties: Map([]),
descriptors: Map([]),
symbol_descriptors: Map([]),
internal_slots: Map::from_array([(SymbolData, Symbol(sym))]),
host_slots: Map([]),
},
prototype: get_symbol_proto(realm_state=Some(realm_state)),
callable: None,
class_name: "Symbol",
extensible: true,
arraybuffer_state: None,
})
_ => value
}
}
///|
// Low-level parameter-default gate snapshot, not an exactly-once cleanup
// capability. Call adapters and the dispatch shell must own LIFO restoration.
priv struct SimpleUserFuncParameterGateScope {
previous_in_nonarrow_param_default_eval : Bool
previous_param_default_eval_var_conflicts : @set.Set[String]?
}
///|
fn SimpleUserFuncParameterGateScope::SimpleUserFuncParameterGateScope(
previous_in_nonarrow_param_default_eval~ : Bool,
previous_param_default_eval_var_conflicts~ : @set.Set[String]?,
) -> SimpleUserFuncParameterGateScope {
{
previous_in_nonarrow_param_default_eval,
previous_param_default_eval_var_conflicts,
}
}
///|
fn Interpreter::begin_simple_user_func_parameter_gate(
self : Interpreter,
) -> SimpleUserFuncParameterGateScope {
let previous_in_nonarrow_param_default_eval = self.in_nonarrow_param_default_eval
let previous_param_default_eval_var_conflicts = self.param_default_eval_var_conflicts
self.in_nonarrow_param_default_eval = false
self.param_default_eval_var_conflicts = None
SimpleUserFuncParameterGateScope(
previous_in_nonarrow_param_default_eval~,
previous_param_default_eval_var_conflicts~,
)
}
///|
fn Interpreter::finish_simple_user_func_parameter_gate(
self : Interpreter,
scope : SimpleUserFuncParameterGateScope,
) -> Unit {
self.in_nonarrow_param_default_eval = scope.previous_in_nonarrow_param_default_eval
self.param_default_eval_var_conflicts = scope.previous_param_default_eval_var_conflicts
}
///|
#warnings("-unused_constructor")
priv enum UserFuncThrowTypeErrorSource {
ResolveInheritedThrowTypeError
UsePreResolvedThrowTypeError(Value?)
}
///|
// Prepared lexical state only. Body scheduling and execution remain owned by
// the caller so the legacy path does not copy the function's statement array.
priv struct PreparedUserFuncActivation {
ctx : ExecContext
env : Environment
}
///|
fn PreparedUserFuncActivation::PreparedUserFuncActivation(
ctx~ : ExecContext,
env~ : Environment,
) -> PreparedUserFuncActivation {
{ ctx, env }
}
///|
fn Interpreter::normalize_sloppy_this(
self : Interpreter,
this_val : Value,
) -> Value {
match this_val {
Undefined | Null => self.global_this
_ => box_primitive_call_this(this_val, self.realm_state)
}
}
///|
// Prepare the lexical activation shared by legacy and resumable simple
// UserFunc adapters. The default source may invoke guest code while resolving
// the inherited [[ThrowTypeError]] binding; routing-neutral callers must pass a
// value resolved before admission.
fn Interpreter::prepare_user_func_activation(
self : Interpreter,
callee : Value,
this_val : Value,
args : Array[Value],
data : FuncData,
throw_type_error_source? : UserFuncThrowTypeErrorSource = ResolveInheritedThrowTypeError,
) -> PreparedUserFuncActivation raise Error {
let func_env = Environment::new(parent=Some(data.closure))
func_env.is_var_scope = true
func_env.def_builtin("[[EvalMethodContext]]", Bool(data.is_method))
let func_strict = data.strict
let func_ctx : ExecContext = { strict: func_strict, current_generator: None }
if func_strict {
// Strict mode: check for duplicate parameters
check_duplicate_params(data.params)
// Strict mode: validate parameter names
for p in data.params {
@static_semantics.validate_strict_binding_name(p)
}
}
let effective_this = if func_strict {
this_val
} else {
self.normalize_sloppy_this(this_val)
}
func_env.def("this", effective_this, LetBinding)
func_env.def("", Undefined, LetBinding)
for i, param in data.params {
let val : Value = if i < args.length() { args[i] } else { Undefined }
// In sloppy mode, duplicate params are allowed; last value wins
if func_env.bindings.contains(param) {
func_env.assign(param, val)
} else {
func_env.def_parameter(param, val)
}
}
if !params_include_arguments(data.params, None) {
// Create arguments object unless a formal parameter is named `arguments`.
let tte_val : Value? = match throw_type_error_source {
// Preserve the legacy lookup order exactly for ordinary callers.
ResolveInheritedThrowTypeError =>
if func_env.has("[[ThrowTypeError]]") {
Some(func_env.get("[[ThrowTypeError]]"))
} else {
None
}
// Trusted callers resolve this before entering the routing-neutral path.
UsePreResolvedThrowTypeError(value) => value
}
func_env.def(
"arguments",
make_arguments_object(
self.realm_state,
self.realm_state.well_known_symbols,
args,
callee,
func_strict,
throw_type_error=tte_val,
mapped_names=data.params,
mapped_env=Some(func_env),
),
VarBinding,
)
}
// Hoist declarations and top-level lexical TDZ markers within the function body.
self.hoist_declarations(data.body, func_env, strict=func_strict)
hoist_block_tdz(data.body, func_env)
if data.has_name_binding {
match data.name {
Some(name) =>
if !func_env.bindings.contains(name) {
func_env.def(name, callee, FunctionNameBinding)
}
None => ()
}
}
PreparedUserFuncActivation(ctx=func_ctx, env=func_env)
}
///|
fn Interpreter::call_value_impl(
self : Interpreter,
callee : Value,
this_val : Value,
args : Array[Value],
loc : @token.Loc,
) -> Value raise Error {
match self.try_tree_executor_admitted_call(callee, this_val, args, loc) {
Some(value) => return value
None => ()
}
match callee {
Proxy(proxy_data) => {
// Verify target chain is callable (recursively unwrap nested proxies)
let target = get_proxy_target(proxy_data)
fn check_callable(v : Value) -> Bool {
match v {
Object(t_data) => t_data.callable is Some(_)
Proxy(pd) =>
match pd.target {
Some(inner) => check_callable(inner)
None => false
}
_ => false
}
}
if !check_callable(target) {
raise @errors.TypeError(message="proxy target is not a function")
}
let trap = get_proxy_trap(proxy_data, "apply", self)
match trap {
Some(trap_fn) => {
let handler = get_proxy_handler(proxy_data)
let args_array : Value = make_array(args.copy())
return self.call_value(
trap_fn,
handler,
[target, this_val, args_array],
loc,
)
}
None => return self.call_value(target, this_val, args, loc)
}
}
Object(obj_data) =>
match obj_data.callable {
Some(UserFunc(data)) => {
// §19.2.1.3 gate reset (#A.6): any function call entry clears
// the signal so a nested invocation (e.g. an IIFE in an outer
// function's param default) does not inherit the outer state.
let gate_scope = self.begin_simple_user_func_parameter_gate()
let result : Value = try {
let prepared = self.prepare_user_func_activation(
callee, this_val, args, data,
)
let exec_result = self.exec_stmts(
prepared.ctx,
data.body,
prepared.env,
)
raise_if_break_continue(exec_result)
match exec_result {
Normal(_) => Undefined
ReturnSignal(v) => v
_ => Undefined
}
} catch {
e => {
self.finish_simple_user_func_parameter_gate(gate_scope)
raise e
}
}
self.finish_simple_user_func_parameter_gate(gate_scope)
result
}
Some(ArrowFunc(data)) => {
// §19.2.1.3 gate reset (#A.6), same rationale as UserFunc above.
let saved_in_default = self.in_nonarrow_param_default_eval
let saved_param_default_conflicts = self.param_default_eval_var_conflicts
self.in_nonarrow_param_default_eval = false
self.param_default_eval_var_conflicts = None
let result : Value = try {
let func_env = Environment::new(parent=Some(data.closure))
func_env.is_var_scope = true
let func_ctx : ExecContext = {
strict: data.strict,
current_generator: None,
}
// Arrow functions do NOT rebind this — use closure's this
for i, param in data.params {
let val : Value = if i < args.length() {
args[i]
} else {
Undefined
}
func_env.def_parameter(param, val)
}
self.hoist_declarations(data.body, func_env, strict=data.strict)
hoist_block_tdz(data.body, func_env)
let exec_result = self.exec_stmts(func_ctx, data.body, func_env)
raise_if_break_continue(exec_result)
match exec_result {
Normal(_) => Undefined
ReturnSignal(v) => v
_ => Undefined
}
} catch {
e => {
self.in_nonarrow_param_default_eval = saved_in_default
self.param_default_eval_var_conflicts = saved_param_default_conflicts
raise e
}
}
self.in_nonarrow_param_default_eval = saved_in_default
self.param_default_eval_var_conflicts = saved_param_default_conflicts
result
}
Some(UserFuncExt(data)) => {
// §15.2.5: for named function expressions, install the self-name
// on a dedicated func_env sitting between data.closure and
// param_env, so default expressions that close over param_env
// can still resolve the name (e.g.
// `var g = function f(a = () => f) { return a(); }`).
// Gated on has_name_binding so methods / class methods /
// function declarations (which also carry a `name`) do NOT get
// the self-binding — per spec those names live in enclosing
// envs, not on a per-call funcEnv.
let self_name_env : Environment = if data.has_name_binding {
match data.name {
Some(name) => {
let ne = Environment::new(parent=Some(data.closure))
ne.def(name, callee, FunctionNameBinding)
ne
}
None => data.closure
}
} else {
data.closure
}
let param_env = Environment::new(parent=Some(self_name_env))
param_env.is_var_scope = true
param_env.def_builtin("[[EvalMethodContext]]", Bool(data.is_method))
let func_strict = data.strict
let func_ctx : ExecContext = {
strict: func_strict,
current_generator: None,
}
if func_strict {
// Strict mode: check for duplicate parameters and reserved names
check_duplicate_params_ext(data.params, data.rest_param)
validate_strict_param_binding_names_ext(
data.params,
data.rest_param,
)
}
let effective_this = if func_strict {
this_val
} else {
self.normalize_sloppy_this(this_val)
}
param_env.def("this", effective_this, LetBinding)
param_env.def("", Undefined, LetBinding)
if !ext_params_include_arguments(data.params, data.rest_param) {
// Create arguments object before binding params so defaults can reference it,
// unless a formal parameter is named `arguments`.
let tte_val2 : Value? = if param_env.has("[[ThrowTypeError]]") {
Some(param_env.get("[[ThrowTypeError]]"))
} else {
None
}
param_env.def(
"arguments",
make_arguments_object(
self.realm_state,
self.realm_state.well_known_symbols,
args,
callee,
func_strict,
throw_type_error=tte_val2,
),
VarBinding,
)
}
// Self-name lives on self_name_env; body-local `var/let/const`
// of the same name naturally shadows via lexical lookup, and a
// param of the same name sits on param_env which shadows too.
self.bind_ext_params_and_exec_body(
data,
args,
param_env,
func_ctx,
is_arrow=false,
)
}
Some(ArrowFuncExt(data)) => {
// Arrows skip `arguments`/this/ install
// (ThisMode=lexical per §10.2.11 step 18) and have no self-name
// wrapper — param_env's parent is just `data.closure`.
let param_env = Environment::new(parent=Some(data.closure))
param_env.is_var_scope = true
let func_ctx : ExecContext = {
strict: data.strict,
current_generator: None,
}
self.bind_ext_params_and_exec_body(
data,
args,
param_env,
func_ctx,
is_arrow=true,
)
}
Some(BoundFunc(target, bound_this, bound_args)) => {
let all_args : Array[Value] = []
for a in bound_args {
all_args.push(a)
}
for a in args {
all_args.push(a)
}
self.call_value(target, bound_this, all_args, loc)
}
Some(FuncCallMethod(_)) =>
raise @errors.TypeError(message="is not a function")
Some(FuncApplyMethod(target)) => {
let effective_target = if target is Undefined {
this_val
} else {
target
}
guard is_callable(effective_target) else {
raise @errors.TypeError(message="is not a function")
}
let call_this = if args.length() > 0 { args[0] } else { Undefined }
let call_args : Array[Value] = []
if args.length() > 1 {
let arg_array = args[1]
match arg_array {
Undefined | Null => ()
_ => {
// Unsupported shapes retain full CreateListFromArrayLike
// semantics: reject primitives, then observe length/index
// through the interpreter so Proxy, accessors, and inherited
// properties stay visible.
guard is_object_value(arg_array) else {
raise @errors.TypeError(
message="CreateListFromArrayLike called on non-object",
)
}
let arg_len = to_array_like_length_interp(arg_array, self)
for i = 0L; i < arg_len; i = i + 1L {
let element = get_array_like_element_interp(
self, arg_array, i,
)
call_args.push(element)
}
}
}
}
self.call_value(effective_target, call_this, call_args, loc)
}
Some(MethodCallable(_, func)) => func(this_val, args)
Some(NativeCallable(_, func)) => func(args)
Some(NativeCallableWithContext(_, func)) => func(Call, args)
Some(NonConstructableCallable(name, func)) =>
if name == "eval" {
// Indirect eval: route through perform_eval for interpreter access
if args.length() == 0 {
Undefined
} else {
match args[0] {
String_(code) => self.perform_eval(code, self.global, false)
other => other
}
}
} else {
func(args)
}
Some(InterpreterCallable(_, func)) => func(self, this_val, args)
Some(InterpreterCallableWithContext(_, func)) =>
func(self, Call, this_val, args)
Some(ExecutorCallable(executable)) =>
self.run_executor_function(executable, callee, Call, this_val, args)
Some(NonConstructableInterpreterCallable(_, func)) => func(self, args)
Some(ConstructorOnlyCallable(name, _)) =>
raise @errors.TypeError(
message=name + " constructor cannot be invoked without 'new'",
)
Some(ClassConstructor({ name, .. })) =>
raise @errors.TypeError(
message="Class constructor " +
name +
" cannot be invoked without 'new'",
)
None => raise @errors.TypeError(message="is not a function")
}
_ => raise @errors.TypeError(message="is not a function")
}
}