Skip to main content

zuqe/function/
bfun.rs

1use gc_lite::{GcRef, GcTracable};
2use smallvec::SmallVec;
3
4use crate::{
5    JS_CLASS_ASYNC_FUNCTION, JS_CLASS_ASYNC_GENERATOR, JS_CLASS_ASYNC_GENERATOR_FUNCTION,
6    JS_CLASS_BYTECODE_FUNCTION, JS_CLASS_GENERATOR, JS_CLASS_GENERATOR_FUNCTION,
7    atom::Atom,
8    class::ClassID,
9    context::{ContextImpl, ContextPtr},
10    error::JSResult,
11    eval::{EvalFlags, EvalType},
12    function::{Bytecode, CallContext, JS_CALL_FLAG_COPY_ARGV, JSVarRef},
13    object::{
14        JS_PROP_CONFIGURABLE, JS_PROP_NORMAL, JS_PROP_TMASK, JSProperty, Object, ObjectImpl,
15        ObjectPayload,
16    },
17    object_cast,
18    runtime::js_check_stack_overflow,
19    stack_frame::StackFrame,
20    value::JSValue,
21};
22
23/// Special index for argument scope.
24/// This is same to `FunctionDef::body_scope`, which was automatically pushed
25/// when creating root function def in `eval_internal()`.
26pub const ARG_SCOPE_INDEX: i32 = 1;
27
28/// to mark end of arugment vars chain.
29pub const ARG_SCOPE_END: i32 = -2;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[repr(u8)]
33pub enum FunctionKind {
34    /// bytecode function
35    Normal = 0,
36    /// generator
37    Generator = 1,
38    /// async function
39    Async = 2,
40    /// async generator.
41    AsyncGenerator = 3,
42}
43
44impl FunctionKind {
45    #[inline(always)]
46    pub const fn is_async(self) -> bool {
47        matches!(self, Self::Async | Self::AsyncGenerator)
48    }
49
50    #[inline(always)]
51    pub const fn is_generator(self) -> bool {
52        matches!(self, Self::Generator | Self::AsyncGenerator)
53    }
54
55    /// get associated class id for function kind
56    #[inline]
57    pub const fn class_id(&self) -> ClassID {
58        match self {
59            Self::Normal => JS_CLASS_BYTECODE_FUNCTION,
60            Self::Generator => JS_CLASS_GENERATOR_FUNCTION,
61            Self::Async => JS_CLASS_ASYNC_FUNCTION,
62            Self::AsyncGenerator => JS_CLASS_ASYNC_GENERATOR_FUNCTION,
63        }
64    }
65
66    #[inline]
67    pub const fn generator_class_id(&self) -> Option<ClassID> {
68        match self {
69            Self::Generator => Some(JS_CLASS_GENERATOR),
70            Self::AsyncGenerator => Some(JS_CLASS_ASYNC_GENERATOR),
71            Self::Normal | Self::Async => None,
72        }
73    }
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77#[repr(u8)]
78pub enum FuncCallType {
79    FUNC_CALL_NORMAL,
80    FUNC_CALL_NEW,
81    FUNC_CALL_SUPER_CTOR,
82    FUNC_CALL_TEMPLATE,
83}
84
85/// Object payload for:
86/// * bytecode function,
87/// * async function,
88/// * generator function,
89/// * async generator function
90#[derive(Debug)]
91pub struct BCFuncPayload {
92    bytecode: Bytecode,
93
94    /// closure variable references
95    pub(crate) var_refs: Vec<JSVarRef>,
96
97    /// 函数对象关联的宿主对象,主要用于以下场景:
98    /// - 私有字段的访问控制:当类方法需要访问私有字段时,需要通过home object定位到对应的私有品牌(private brand)进行权限验证。
99    /// - for 'super' access
100    home_object: Option<Object>,
101}
102
103unsafe impl GcTracable for BCFuncPayload {
104    #[inline(always)]
105    fn trace(&self, tracer: &mut gc_lite::GcTracer) {
106        self.bytecode.trace(tracer);
107        self.var_refs.iter().for_each(|x| x.trace(tracer));
108        self.home_object.trace(tracer);
109    }
110}
111
112impl BCFuncPayload {
113    pub fn new(bytecode: Bytecode, var_refs: Vec<JSVarRef>) -> Box<Self> {
114        Box::new(Self {
115            bytecode,
116            var_refs,
117            home_object: None,
118        })
119    }
120
121    /// get bytecode
122    #[inline(always)]
123    pub const fn bytecode(&self) -> Bytecode {
124        self.bytecode
125    }
126
127    #[inline(always)]
128    pub const fn home_object(&self) -> Option<Object> {
129        self.home_object
130    }
131
132    pub(crate) const fn set_home_object(&mut self, obj: Option<Object>) {
133        self.home_object = obj;
134    }
135}
136
137/// represents a bytecode function, generator function, async function or async generator function,
138/// which have bytecode.
139#[derive(Debug, Clone, Copy)]
140#[repr(transparent)]
141pub struct BCFunction(pub(super) GcRef<ObjectImpl>);
142
143object_cast!(
144    BCFunction,
145    JS_CLASS_BYTECODE_FUNCTION
146        | JS_CLASS_GENERATOR_FUNCTION
147        | JS_CLASS_ASYNC_FUNCTION
148        | JS_CLASS_ASYNC_GENERATOR_FUNCTION
149);
150
151impl BCFunction {
152    #[inline(always)]
153    pub fn is_async(&self) -> bool {
154        matches!(
155            self.class_id(),
156            JS_CLASS_ASYNC_FUNCTION | JS_CLASS_ASYNC_GENERATOR_FUNCTION
157        )
158    }
159
160    #[inline(always)]
161    pub fn is_generator(&self) -> bool {
162        matches!(
163            self.class_id(),
164            JS_CLASS_GENERATOR_FUNCTION | JS_CLASS_ASYNC_GENERATOR_FUNCTION
165        )
166    }
167
168    /// get bcfunction payload
169    #[inline]
170    pub fn payload(&self) -> &BCFuncPayload {
171        unsafe { self.0.as_ref().payload().bc_function().unwrap() }
172    }
173
174    /// get bcfunction payload
175    #[inline]
176    pub fn payload_mut(&mut self) -> &mut BCFuncPayload {
177        unsafe { self.0.as_mut().payload_mut().bc_function_mut().unwrap() }
178    }
179
180    pub(crate) fn method_set_home_object(&mut self, home_obj: Option<Object>) {
181        let fo = self.payload_mut();
182        if fo.bytecode().need_home_object() {
183            fo.set_home_object(home_obj);
184        }
185    }
186}
187
188impl ObjectImpl {
189    /// check if `self` is a function or a callable object.
190    /// raise exception if not.
191    pub fn ensure_function(&self, ctx: &ContextImpl) -> Result<&Self, i32> {
192        if self.is_function() {
193            Ok(self)
194        } else {
195            ctx.throw_type_error("not a function");
196            Err(-1)
197        }
198    }
199
200    /// return realm context of the function object
201    pub(crate) fn realm(&self, default: ContextPtr) -> Result<ContextPtr, i32> {
202        match self.payload() {
203            ObjectPayload::C_FUNCTION(cfunc) => Ok(cfunc
204                .realm
205                .upgrade(default.rt_ptr().gc_heap())
206                .map_or(default, ContextPtr::from)),
207            ObjectPayload::BYTECODE_FUNCTION(fo)
208            | ObjectPayload::GENERATOR_FUNCTION(fo)
209            | ObjectPayload::ASYNC_FUNCTION(fo)
210            | ObjectPayload::ASYNC_GENERATOR_FUNCTION(fo) => {
211                Ok(fo.bytecode().realm().unwrap_or(default))
212            }
213            ObjectPayload::PROXY(px) => {
214                if px.is_revoked() {
215                    default.throw_type_error_revoked_proxy();
216                    Err(-1)
217                } else {
218                    px.target().realm(default)
219                }
220            }
221            ObjectPayload::BOUND_FUNCTION(bfunc) => {
222                if let Some(o) = bfunc.func_obj.as_object() {
223                    o.realm(default)
224                } else {
225                    Ok(default)
226                }
227            }
228            _ => Ok(default),
229        }
230    }
231}
232
233impl JSValue {
234    /// check if value `self` is a function or a callable object.
235    /// raise exception if not.
236    pub fn ensure_function(&self, ctx: &ContextImpl) -> Result<&Self, i32> {
237        if self.is_function() {
238            Ok(self)
239        } else {
240            ctx.throw_type_error("not a function");
241            Err(-1)
242        }
243    }
244}
245
246pub fn get_func_name(func: JSValue) -> Option<String> {
247    // in order to avoid executing arbitrary code during the stack trace generation,
248    // we only look at simple 'name' properties containing a string.
249    if let Some(obj) = func.as_object() {
250        if let Some((idx, prs_flags)) = obj.find_shape_property(Atom::CONST_name)
251            && prs_flags & JS_PROP_TMASK == JS_PROP_NORMAL
252        {
253            if let Some(JSProperty::Normal(JSValue::String(s))) = obj.prop_values.get(idx) {
254                return Some(s.content().encode_utf8(false).into_owned());
255            } else {
256                #[cfg(debug_assertions)]
257                unreachable!();
258                #[cfg(not(debug_assertions))]
259                unsafe {
260                    std::hint::unreachable_unchecked();
261                }
262            }
263        }
264
265        None
266    } else {
267        None
268    }
269}
270
271/// define `length` and `name` properties on function object `func_obj`
272pub fn js_function_set_properties(ctx: &ContextImpl, mut func_obj: Object, name: Atom, len: i32) {
273    let _ = func_obj.define_property_value(
274        ctx,
275        Atom::CONST_length,
276        JSValue::Int(len),
277        JS_PROP_CONFIGURABLE, // ES6 feature non-compatible with ES5.1: length is configurable
278    );
279    let _ = func_obj.define_property_value(
280        ctx,
281        Atom::CONST_name,
282        name.force_string(ctx)
283            .map_or(JSValue::EXCEPTION, JSValue::String),
284        JS_PROP_CONFIGURABLE,
285    );
286}
287
288pub fn js_function_proto(_: CallContext) -> JSResult {
289    Ok(JSValue::UNDEFINED)
290}
291
292#[repr(transparent)]
293#[derive(Debug, Clone, Copy)]
294pub struct BytecodeFunction(GcRef<ObjectImpl>);
295
296object_cast!(BytecodeFunction, JS_CLASS_BYTECODE_FUNCTION);
297
298impl BytecodeFunction {
299    #[inline(always)]
300    pub fn payload(&self) -> &BCFuncPayload {
301        unsafe { self.0.as_ref().payload().bc_function().unwrap() }
302    }
303
304    #[inline(always)]
305    pub fn payload_mut(&mut self) -> &mut BCFuncPayload {
306        unsafe { self.0.as_mut().payload_mut().bc_function_mut().unwrap() }
307    }
308
309    /// call bytecode function
310    pub fn call(
311        mut self,
312        caller_ctx: &ContextImpl,
313        this_obj: JSValue,
314        new_target: JSValue,
315        args: &[JSValue],
316        flags: i32,
317    ) -> JSValue {
318        let rt = caller_ctx.rt_ptr();
319        let bc = self.payload().bytecode();
320
321        // how many args that should be copied to local buf?
322        // `args.len()` is actual argument count
323        // `b.arg_count` is the length when defining the function
324        let arg_copy_num =
325            if flags & JS_CALL_FLAG_COPY_ARGV != 0 || args.len() < bc.arg_count as usize {
326                std::cmp::max(args.len(), bc.arg_count as usize)
327            } else {
328                0
329            };
330
331        // how many bytes to allocate for local buf (args+vars+stack)?
332        let (buf_values_count, js_mode) = (
333            arg_copy_num as usize + bc.var_count as usize + bc.stack_size as usize,
334            bc.mode(),
335        );
336
337        let mut this_frame = StackFrame::new(self.into(), js_mode);
338        let sf = &mut this_frame;
339
340        // ensure stack has minimal free space.
341        if js_check_stack_overflow(&rt, 32) {
342            caller_ctx.throw_stack_overflow();
343            return JSValue::EXCEPTION;
344        }
345
346        // make local buffer and running bytecodes with it.
347        // local_buf layout:
348        //
349        // +------------------+
350        // | argument values  | <- arg_buf (optional, if need to copy arg)
351        // | variable values  | <- var_buf
352        // | stack space      | <- stack_buf
353        // +------------------+
354        //
355        if sf.local_buf.try_reserve_exact(buf_values_count).is_err() {
356            caller_ctx.throw_out_of_memory();
357            return JSValue::EXCEPTION;
358        }
359
360        // initialize args section
361        if arg_copy_num != 0 {
362            debug_assert!(arg_copy_num >= args.len());
363
364            // copy actual arg values
365            sf.local_buf.extend(args.iter().cloned());
366            // padding with undefined
367            sf.local_buf.extend(std::iter::repeat_n(
368                JSValue::UNDEFINED,
369                arg_copy_num - args.len(),
370            ));
371
372            sf.arg_count = arg_copy_num as _;
373            sf.arg_real_count = args.len() as _;
374            sf.arg_base_ptr = sf.local_buf.as_mut_ptr()
375        } else {
376            sf.arg_count = args.len() as _;
377            sf.arg_real_count = args.len() as _;
378            sf.arg_base_ptr = args.as_ptr().cast::<JSValue>() as *mut _;
379        }
380
381        // initialize (vars + stack) section
382        sf.local_buf.extend(std::iter::repeat_n(
383            JSValue::UNDEFINED,
384            (bc.var_count + bc.stack_size) as usize,
385        ));
386        debug_assert_eq!(sf.local_buf.len(), buf_values_count);
387
388        sf.var_base_ptr = unsafe { sf.local_buf.as_mut_ptr().add(arg_copy_num as usize) };
389        sf.var_count = bc.var_count;
390
391        let isa = self.payload_mut();
392        bc.run(
393            caller_ctx,
394            isa.home_object,
395            sf,
396            this_obj,
397            new_target,
398            0,
399            0,
400            &mut isa.var_refs,
401            args,
402            false,
403        )
404    }
405
406    /// handle `Function` constructor. For example:
407    ///
408    /// ```js
409    /// const f = new Function("a", "b", "return a+b")
410    /// ```
411    pub fn class_constructor(cc: CallContext) -> JSResult {
412        debug_assert!((0..4).contains(&cc.magic()));
413        let mut ctx = cc.ctx_ptr();
414
415        // XXX: add a specific eval mode so that Function("}), ({") is rejected
416
417        let new_target = *cc.this();
418        let func_kind = unsafe { std::mem::transmute::<u8, FunctionKind>(cc.magic() as u8) };
419
420        //
421        // make function source script:
422        // ([async] function[*] anonymous(a,b){<body>})
423        //
424        let mut buf = SmallVec::<[u8; 256]>::new();
425        buf.push(b'(');
426
427        if matches!(
428            func_kind,
429            FunctionKind::Async | FunctionKind::AsyncGenerator
430        ) {
431            buf.extend_from_slice(b"async ");
432        }
433
434        buf.extend_from_slice(b"function");
435        if matches!(
436            func_kind,
437            FunctionKind::Generator | FunctionKind::AsyncGenerator
438        ) {
439            buf.push(b'*');
440        }
441
442        buf.extend_from_slice(b" anonymous(");
443
444        // Note: the last argument is function body.
445        if let Some((body, arglist)) = cc.arguments().split_last() {
446            // args list
447            for (i, a) in arglist.iter().enumerate() {
448                if i != 0 {
449                    buf.push(b',');
450                }
451                buf.extend_from_slice(a.to_string(&ctx)?.content().encode_utf8(false).as_bytes());
452            }
453            buf.extend_from_slice(b"\n) {\n");
454
455            // function body
456            buf.extend_from_slice(
457                body.to_string(&ctx)?
458                    .content()
459                    .encode_utf8(false)
460                    .as_bytes(),
461            );
462        } else {
463            // no arguments
464            buf.extend_from_slice(b") {\n");
465        }
466
467        buf.extend_from_slice(b"\n})");
468
469        // eval script
470        let mut func_val = {
471            let global = ctx.global_obj();
472            ctx.eval_internal(
473                global.into(),
474                buf.as_slice(),
475                "<input>",
476                EvalType::Indirect,
477                EvalFlags::empty(),
478                -1,
479            )?
480        };
481
482        if !new_target.is_undefined() {
483            // set the prototype
484            let mut proto = new_target.get_property(&ctx, Atom::CONST_prototype)?;
485            if !proto.is_object() {
486                proto = new_target
487                    .as_object()
488                    .map_or(Ok(ctx), |o| o.realm(ctx))?
489                    .class_proto(func_kind.class_id())
490                    .map_or(JSValue::NULL, JSValue::Object);
491            }
492
493            func_val.set_prototype_internal(&ctx, proto, true)?;
494        }
495
496        Ok(func_val)
497    }
498}