|
3 | 3 | //! The `GcObject` is a garbage collected Object.
|
4 | 4 |
|
5 | 5 | use super::Object;
|
| 6 | +use crate::{ |
| 7 | + builtins::{ |
| 8 | + function::{create_unmapped_arguments_object, BuiltInFunction, Function}, |
| 9 | + ResultValue, Value, |
| 10 | + }, |
| 11 | + environment::{ |
| 12 | + function_environment_record::BindingStatus, lexical_environment::new_function_environment, |
| 13 | + }, |
| 14 | + Executable, Interpreter, |
| 15 | +}; |
6 | 16 | use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
|
7 | 17 | use std::fmt::{self, Display};
|
8 | 18 |
|
@@ -41,6 +51,151 @@ impl GcObject {
|
41 | 51 | pub fn equals(lhs: &Self, rhs: &Self) -> bool {
|
42 | 52 | std::ptr::eq(lhs.as_ref(), rhs.as_ref())
|
43 | 53 | }
|
| 54 | + |
| 55 | + /// This will handle calls for both ordinary and built-in functions |
| 56 | + /// |
| 57 | + /// <https://tc39.es/ecma262/#sec-prepareforordinarycall> |
| 58 | + /// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist> |
| 59 | + pub fn call(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
| 60 | + let this_function_object = self.clone(); |
| 61 | + let object = self.borrow(); |
| 62 | + if let Some(function) = object.as_function() { |
| 63 | + if function.is_callable() { |
| 64 | + match function { |
| 65 | + Function::BuiltIn(BuiltInFunction(function), _) => function(this, args, ctx), |
| 66 | + Function::Ordinary { |
| 67 | + body, |
| 68 | + params, |
| 69 | + environment, |
| 70 | + flags, |
| 71 | + } => { |
| 72 | + // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment) |
| 73 | + // <https://tc39.es/ecma262/#sec-prepareforordinarycall> |
| 74 | + let local_env = new_function_environment( |
| 75 | + this_function_object, |
| 76 | + if flags.is_lexical_this_mode() { |
| 77 | + None |
| 78 | + } else { |
| 79 | + Some(this.clone()) |
| 80 | + }, |
| 81 | + Some(environment.clone()), |
| 82 | + // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records |
| 83 | + if flags.is_lexical_this_mode() { |
| 84 | + BindingStatus::Lexical |
| 85 | + } else { |
| 86 | + BindingStatus::Uninitialized |
| 87 | + }, |
| 88 | + ); |
| 89 | + |
| 90 | + // Add argument bindings to the function environment |
| 91 | + for (i, param) in params.iter().enumerate() { |
| 92 | + // Rest Parameters |
| 93 | + if param.is_rest_param() { |
| 94 | + function.add_rest_param(param, i, args, ctx, &local_env); |
| 95 | + break; |
| 96 | + } |
| 97 | + |
| 98 | + let value = args.get(i).cloned().unwrap_or_else(Value::undefined); |
| 99 | + function.add_arguments_to_environment(param, value, &local_env); |
| 100 | + } |
| 101 | + |
| 102 | + // Add arguments object |
| 103 | + let arguments_obj = create_unmapped_arguments_object(args); |
| 104 | + local_env |
| 105 | + .borrow_mut() |
| 106 | + .create_mutable_binding("arguments".to_string(), false); |
| 107 | + local_env |
| 108 | + .borrow_mut() |
| 109 | + .initialize_binding("arguments", arguments_obj); |
| 110 | + |
| 111 | + ctx.realm.environment.push(local_env); |
| 112 | + |
| 113 | + // Call body should be set before reaching here |
| 114 | + let result = body.run(ctx); |
| 115 | + |
| 116 | + // local_env gets dropped here, its no longer needed |
| 117 | + ctx.realm.environment.pop(); |
| 118 | + result |
| 119 | + } |
| 120 | + } |
| 121 | + } else { |
| 122 | + ctx.throw_type_error("function object is not callable") |
| 123 | + } |
| 124 | + } else { |
| 125 | + ctx.throw_type_error("not a function") |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + /// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget> |
| 130 | + pub fn construct(&self, this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { |
| 131 | + let this_function_object = self.clone(); |
| 132 | + let object = self.borrow(); |
| 133 | + if let Some(function) = object.as_function() { |
| 134 | + if function.is_constructable() { |
| 135 | + match function { |
| 136 | + Function::BuiltIn(BuiltInFunction(function), _) => { |
| 137 | + function(this, args, ctx)?; |
| 138 | + Ok(this.clone()) |
| 139 | + } |
| 140 | + Function::Ordinary { |
| 141 | + body, |
| 142 | + params, |
| 143 | + environment, |
| 144 | + flags, |
| 145 | + } => { |
| 146 | + // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment) |
| 147 | + // <https://tc39.es/ecma262/#sec-prepareforordinarycall> |
| 148 | + let local_env = new_function_environment( |
| 149 | + this_function_object, |
| 150 | + Some(this.clone()), |
| 151 | + Some(environment.clone()), |
| 152 | + // Arrow functions do not have a this binding https://tc39.es/ecma262/#sec-function-environment-records |
| 153 | + if flags.is_lexical_this_mode() { |
| 154 | + BindingStatus::Lexical |
| 155 | + } else { |
| 156 | + BindingStatus::Uninitialized |
| 157 | + }, |
| 158 | + ); |
| 159 | + |
| 160 | + // Add argument bindings to the function environment |
| 161 | + for (i, param) in params.iter().enumerate() { |
| 162 | + // Rest Parameters |
| 163 | + if param.is_rest_param() { |
| 164 | + function.add_rest_param(param, i, args, ctx, &local_env); |
| 165 | + break; |
| 166 | + } |
| 167 | + |
| 168 | + let value = args.get(i).cloned().unwrap_or_else(Value::undefined); |
| 169 | + function.add_arguments_to_environment(param, value, &local_env); |
| 170 | + } |
| 171 | + |
| 172 | + // Add arguments object |
| 173 | + let arguments_obj = create_unmapped_arguments_object(args); |
| 174 | + local_env |
| 175 | + .borrow_mut() |
| 176 | + .create_mutable_binding("arguments".to_string(), false); |
| 177 | + local_env |
| 178 | + .borrow_mut() |
| 179 | + .initialize_binding("arguments", arguments_obj); |
| 180 | + |
| 181 | + ctx.realm.environment.push(local_env); |
| 182 | + |
| 183 | + // Call body should be set before reaching here |
| 184 | + let _ = body.run(ctx); |
| 185 | + |
| 186 | + // local_env gets dropped here, its no longer needed |
| 187 | + let binding = ctx.realm.environment.get_this_binding(); |
| 188 | + Ok(binding) |
| 189 | + } |
| 190 | + } |
| 191 | + } else { |
| 192 | + let name = this.get_field("name").display().to_string(); |
| 193 | + ctx.throw_type_error(format!("{} is not a constructor", name)) |
| 194 | + } |
| 195 | + } else { |
| 196 | + ctx.throw_type_error("not a function") |
| 197 | + } |
| 198 | + } |
44 | 199 | }
|
45 | 200 |
|
46 | 201 | impl AsRef<GcCell<Object>> for GcObject {
|
|
0 commit comments