diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs
index 4cf03d37901..76a0fa4a932 100644
--- a/boa/src/builtins/array/mod.rs
+++ b/boa/src/builtins/array/mod.rs
@@ -16,7 +16,7 @@ use super::function::{make_builtin_fn, make_constructor_fn};
 use crate::{
     builtins::{
         error::RangeError,
-        object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
+        object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
         property::Property,
         value::{same_value_zero, ResultValue, Value, ValueData},
     },
@@ -42,7 +42,7 @@ impl Array {
                 .get_global_object()
                 .expect("Could not get global object"),
         ));
-        array.set_kind(ObjectKind::Array);
+        array.set_data(ObjectData::Array);
         array.borrow().set_internal_slot(
             INSTANCE_PROTOTYPE,
             interpreter
@@ -117,7 +117,7 @@ impl Array {
         this.set_internal_slot(INSTANCE_PROTOTYPE, prototype);
         // This value is used by console.log and other routines to match Object type
         // to its Javascript Identifier (global constructor method name)
-        this.set_kind(ObjectKind::Array);
+        this.set_data(ObjectData::Array);
 
         // add our arguments in
         let mut length = args.len() as i32;
@@ -176,7 +176,7 @@ impl Array {
                     // 1.
                     ValueData::Object(ref obj) => {
                         // 2.
-                        if (*obj).deref().borrow().kind == ObjectKind::Array {
+                        if let ObjectData::Array = (*obj).deref().borrow().data {
                             return Ok(value_true);
                         }
                         Ok(value_false)
diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs
index f01b4573c6a..b2206b8fb7f 100644
--- a/boa/src/builtins/bigint/mod.rs
+++ b/boa/src/builtins/bigint/mod.rs
@@ -40,15 +40,11 @@ impl BigInt {
     ///
     /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
     /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
-    pub(crate) fn make_bigint(
-        _this: &mut Value,
-        args: &[Value],
-        ctx: &mut Interpreter,
-    ) -> ResultValue {
+    pub(crate) fn make_bigint(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
         let data = match args.get(0) {
             Some(ref value) => {
                 if let Some(bigint) = value.to_bigint() {
-                    Value::from(bigint)
+                    bigint
                 } else {
                     return Err(RangeError::run_new(
                         format!(
@@ -59,9 +55,9 @@ impl BigInt {
                     )?);
                 }
             }
-            None => Value::from(AstBigInt::from(0)),
+            None => AstBigInt::from(0),
         };
-        Ok(data)
+        Ok(Value::from(data))
     }
 
     /// `BigInt.prototype.toString( [radix] )`
@@ -119,7 +115,6 @@ impl BigInt {
     /// Create a new `Number` object
     pub(crate) fn create(global: &Value) -> Value {
         let prototype = Value::new_object(Some(global));
-        prototype.set_internal_slot("BigIntData", Value::from(AstBigInt::from(0)));
 
         make_builtin_fn(Self::to_string, "toString", &prototype, 1);
         make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs
index 40b7de056dd..f8895ae2816 100644
--- a/boa/src/builtins/boolean/mod.rs
+++ b/boa/src/builtins/boolean/mod.rs
@@ -15,7 +15,7 @@ mod tests;
 use super::function::{make_builtin_fn, make_constructor_fn};
 use crate::{
     builtins::{
-        object::{internal_methods_trait::ObjectInternalMethods, ObjectKind},
+        object::ObjectData,
         value::{ResultValue, Value, ValueData},
     },
     exec::Interpreter,
@@ -35,19 +35,11 @@ impl Boolean {
         args: &[Value],
         _: &mut Interpreter,
     ) -> ResultValue {
-        this.set_kind(ObjectKind::Boolean);
-
         // Get the argument, if any
-        if let Some(ref value) = args.get(0) {
-            this.set_internal_slot("BooleanData", Self::to_boolean(value));
-        } else {
-            this.set_internal_slot("BooleanData", Self::to_boolean(&Value::from(false)));
-        }
+        let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
+        this.set_data(ObjectData::Boolean(data));
 
-        match args.get(0) {
-            Some(ref value) => Ok(Self::to_boolean(value)),
-            None => Ok(Self::to_boolean(&Value::from(false))),
-        }
+        Ok(Value::from(data))
     }
 
     /// The `toString()` method returns a string representing the specified `Boolean` object.
@@ -73,22 +65,7 @@ impl Boolean {
     /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
     /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
     pub(crate) fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
-        Ok(Self::this_boolean_value(this))
-    }
-
-    // === Utility Functions ===
-    /// [toBoolean](https://tc39.es/ecma262/#sec-toboolean)
-    /// Creates a new boolean value from the input
-    #[allow(clippy::wrong_self_convention)]
-    pub(crate) fn to_boolean(value: &Value) -> Value {
-        match *value.deref().borrow() {
-            ValueData::Object(_) => Value::from(true),
-            ValueData::String(ref s) if !s.is_empty() => Value::from(true),
-            ValueData::Rational(n) if n != 0.0 && !n.is_nan() => Value::from(true),
-            ValueData::Integer(n) if n != 0 => Value::from(true),
-            ValueData::Boolean(v) => Value::from(v),
-            _ => Value::from(false),
-        }
+        Ok(Value::from(Self::this_boolean_value(this)))
     }
 
     /// An Utility function used to get the internal BooleanData.
@@ -97,11 +74,14 @@ impl Boolean {
     ///  - [ECMAScript reference][spec]
     ///
     /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
-    pub(crate) fn this_boolean_value(value: &Value) -> Value {
+    pub(crate) fn this_boolean_value(value: &Value) -> bool {
         match *value.deref().borrow() {
-            ValueData::Boolean(v) => Value::from(v),
-            ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"),
-            _ => Value::from(false),
+            ValueData::Boolean(v) => v,
+            ValueData::Object(ref v) => match v.deref().borrow().data {
+                ObjectData::Boolean(boolean) => boolean,
+                _ => unreachable!(),
+            },
+            _ => false,
         }
     }
 
@@ -110,7 +90,6 @@ impl Boolean {
         // Create Prototype
         // https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
         let prototype = Value::new_object(Some(global));
-        prototype.set_internal_slot("BooleanData", Self::to_boolean(&Value::from(false)));
 
         make_builtin_fn(Self::to_string, "toString", &prototype, 0);
         make_builtin_fn(Self::value_of, "valueOf", &prototype, 0);
diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs
index 328edc50bfb..bea435ea65b 100644
--- a/boa/src/builtins/error/mod.rs
+++ b/boa/src/builtins/error/mod.rs
@@ -13,7 +13,7 @@
 use crate::{
     builtins::{
         function::{make_builtin_fn, make_constructor_fn},
-        object::ObjectKind,
+        object::ObjectData,
         value::{ResultValue, Value},
     },
     exec::Interpreter,
@@ -47,7 +47,7 @@ impl Error {
         }
         // This value is used by console.log and other routines to match Object type
         // to its Javascript Identifier (global constructor method name)
-        this.set_kind(ObjectKind::Error);
+        this.set_data(ObjectData::Error);
         Ok(Value::undefined())
     }
 
diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs
index 34e4c88ff77..1fc3789219c 100644
--- a/boa/src/builtins/error/range.rs
+++ b/boa/src/builtins/error/range.rs
@@ -13,7 +13,7 @@ use crate::{
     builtins::{
         function::make_builtin_fn,
         function::make_constructor_fn,
-        object::ObjectKind,
+        object::ObjectData,
         value::{ResultValue, Value},
     },
     exec::Interpreter,
@@ -38,7 +38,7 @@ impl RangeError {
         }
         // This value is used by console.log and other routines to match Object type
         // to its Javascript Identifier (global constructor method name)
-        this.set_kind(ObjectKind::Error);
+        this.set_data(ObjectData::Error);
         Ok(Value::undefined())
     }
 
diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs
index 2886b97051f..d678ce6e100 100644
--- a/boa/src/builtins/function/mod.rs
+++ b/boa/src/builtins/function/mod.rs
@@ -14,7 +14,7 @@
 use crate::{
     builtins::{
         array::Array,
-        object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
+        object::{Object, ObjectData, ObjectInternalMethods, INSTANCE_PROTOTYPE, PROTOTYPE},
         property::Property,
         value::{ResultValue, Value},
     },
@@ -55,7 +55,7 @@ pub enum FunctionBody {
 impl Debug for FunctionBody {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            Self::BuiltIn(_) => write!(f, "native code"),
+            Self::BuiltIn(_) => write!(f, "[native]"),
             Self::Ordinary(statements) => write!(f, "{:?}", statements),
         }
     }
@@ -158,19 +158,19 @@ impl Function {
     /// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>
     pub fn call(
         &self,
-        this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
+        function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
+        this: &mut Value,
         args_list: &[Value],
         interpreter: &mut Interpreter,
-        this_obj: &mut Value,
     ) -> ResultValue {
         if self.callable {
             match self.body {
-                FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
+                FunctionBody::BuiltIn(func) => func(this, args_list, interpreter),
                 FunctionBody::Ordinary(ref body) => {
                     // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
                     // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
                     let local_env = new_function_environment(
-                        this.clone(),
+                        function,
                         None,
                         Some(self.environment.as_ref().unwrap().clone()),
                         BindingStatus::Uninitialized,
@@ -216,23 +216,23 @@ impl Function {
     /// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>
     pub fn construct(
         &self,
-        this: &mut Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
+        function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
+        this: &mut Value,
         args_list: &[Value],
         interpreter: &mut Interpreter,
-        this_obj: &mut Value,
     ) -> ResultValue {
         if self.constructable {
             match self.body {
                 FunctionBody::BuiltIn(func) => {
-                    func(this_obj, args_list, interpreter).unwrap();
-                    Ok(this_obj.clone())
+                    func(this, args_list, interpreter)?;
+                    Ok(this.clone())
                 }
                 FunctionBody::Ordinary(ref body) => {
                     // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
                     // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
                     let local_env = new_function_environment(
-                        this.clone(),
-                        Some(this_obj.clone()),
+                        function,
+                        Some(this.clone()),
                         Some(self.environment.as_ref().unwrap().clone()),
                         BindingStatus::Initialized,
                     );
@@ -368,7 +368,10 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
 ///
 // This gets called when a new Function() is created.
 pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
-    this.set_kind(ObjectKind::Function);
+    this.set_data(ObjectData::Function(Function::builtin(
+        Vec::new(),
+        |_, _, _| Ok(Value::undefined()),
+    )));
     Ok(this.clone())
 }
 
@@ -399,8 +402,7 @@ pub fn make_constructor_fn(
     let func_prototype = global.get_field("Function").get_field(PROTOTYPE);
 
     // Create the function object and point its instance prototype to Function.prototype
-    let mut constructor_obj = Object::function();
-    constructor_obj.set_func(constructor_fn);
+    let mut constructor_obj = Object::function(constructor_fn);
 
     constructor_obj.set_internal_slot(INSTANCE_PROTOTYPE, func_prototype);
     let constructor_val = Value::from(constructor_obj);
@@ -433,12 +435,9 @@ pub fn make_builtin_fn<N>(function: NativeFunctionData, name: N, parent: &Value,
 where
     N: Into<String>,
 {
-    let func = Function::builtin(Vec::new(), function);
+    let func = Object::function(Function::builtin(Vec::new(), function));
 
-    let mut new_func = Object::function();
-    new_func.set_func(func);
-
-    let new_func_obj = Value::from(new_func);
+    let new_func_obj = Value::from(func);
     new_func_obj.set_field("length", length);
 
     parent.set_field(name.into(), new_func_obj);
diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs
index 05cde1dcf13..06e9bddfd83 100644
--- a/boa/src/builtins/number/mod.rs
+++ b/boa/src/builtins/number/mod.rs
@@ -18,18 +18,17 @@ mod tests;
 
 use super::{
     function::{make_builtin_fn, make_constructor_fn},
-    object::ObjectKind,
+    object::ObjectData,
 };
 use crate::{
     builtins::{
-        object::internal_methods_trait::ObjectInternalMethods,
         value::{ResultValue, Value, ValueData},
         RangeError,
     },
     exec::Interpreter,
 };
 use num_traits::float::FloatCore;
-use std::{borrow::Borrow, f64, ops::Deref};
+use std::{f64, ops::Deref};
 
 const BUF_SIZE: usize = 2200;
 
@@ -41,19 +40,22 @@ impl Number {
     /// Helper function that converts a Value to a Number.
     #[allow(clippy::wrong_self_convention)]
     fn to_number(value: &Value) -> Value {
-        match *value.deref().borrow() {
+        match value.data() {
             ValueData::Boolean(b) => {
-                if b {
+                if *b {
                     Value::from(1)
                 } else {
                     Value::from(0)
                 }
             }
             ValueData::Symbol(_) | ValueData::Undefined => Value::from(f64::NAN),
-            ValueData::Integer(i) => Value::from(f64::from(i)),
-            ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"),
+            ValueData::Integer(i) => Value::from(f64::from(*i)),
+            ValueData::Object(ref o) => match (o).deref().borrow().data {
+                ObjectData::Number(num) => Value::from(num),
+                _ => unreachable!(),
+            },
             ValueData::Null => Value::from(0),
-            ValueData::Rational(n) => Value::from(n),
+            ValueData::Rational(n) => Value::from(*n),
             ValueData::BigInt(ref bigint) => Value::from(bigint.to_f64()),
             ValueData::String(ref s) => match s.parse::<f64>() {
                 Ok(n) => Value::from(n),
@@ -80,13 +82,12 @@ impl Number {
         _ctx: &mut Interpreter,
     ) -> ResultValue {
         let data = match args.get(0) {
-            Some(ref value) => Self::to_number(value),
-            None => Self::to_number(&Value::from(0)),
+            Some(ref value) => Self::to_number(value).to_number(),
+            None => 0.0,
         };
-        this.set_kind(ObjectKind::Number);
-        this.set_internal_slot("NumberData", data.clone());
+        this.set_data(ObjectData::Number(data));
 
-        Ok(data)
+        Ok(Value::from(data))
     }
 
     /// `Number.prototype.toExponential( [fractionDigits] )`
@@ -393,7 +394,6 @@ impl Number {
     /// Create a new `Number` object
     pub(crate) fn create(global: &Value) -> Value {
         let prototype = Value::new_object(Some(global));
-        prototype.set_internal_slot("NumberData", Value::from(0));
 
         make_builtin_fn(Self::to_exponential, "toExponential", &prototype, 1);
         make_builtin_fn(Self::to_fixed, "toFixed", &prototype, 1);
diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs
index 912d15203cd..7d24d7595b7 100644
--- a/boa/src/builtins/object/mod.rs
+++ b/boa/src/builtins/object/mod.rs
@@ -20,12 +20,12 @@ use crate::{
         value::{same_value, ResultValue, Value, ValueData},
     },
     exec::Interpreter,
+    syntax::ast::bigint::BigInt as AstBigInt,
 };
-use gc::{unsafe_empty_trace, Finalize, Trace};
+use gc::{Finalize, Trace};
 use rustc_hash::FxHashMap;
 use std::{
-    borrow::Borrow,
-    fmt::{self, Debug, Display, Error, Formatter},
+    fmt::{Debug, Display, Error, Formatter},
     ops::Deref,
 };
 
@@ -43,10 +43,10 @@ pub static PROTOTYPE: &str = "prototype";
 pub static INSTANCE_PROTOTYPE: &str = "__proto__";
 
 /// The internal representation of an JavaScript object.
-#[derive(Trace, Finalize, Clone)]
+#[derive(Debug, Trace, Finalize, Clone)]
 pub struct Object {
     /// The type of the object.
-    pub kind: ObjectKind,
+    pub data: ObjectData,
     /// Internal Slots
     pub internal_slots: FxHashMap<String, Value>,
     /// Properties
@@ -55,23 +55,6 @@ pub struct Object {
     pub sym_properties: FxHashMap<i32, Property>,
     /// Some rust object that stores internal state
     pub state: Option<InternalStateCell>,
-    /// Function
-    pub func: Option<Function>,
-}
-
-impl Debug for Object {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        writeln!(f, "{{")?;
-        writeln!(f, "\tkind: {}", self.kind)?;
-        writeln!(f, "\tstate: {:?}", self.state)?;
-        writeln!(f, "\tfunc: {:?}", self.func)?;
-        writeln!(f, "\tproperties: {{")?;
-        for (key, _) in self.properties.iter() {
-            writeln!(f, "\t\t{}", key)?;
-        }
-        writeln!(f, "\t }}")?;
-        write!(f, "}}")
-    }
 }
 
 impl ObjectInternalMethods for Object {
@@ -335,12 +318,11 @@ impl Object {
     /// Return a new ObjectData struct, with `kind` set to Ordinary
     pub fn default() -> Self {
         let mut object = Self {
-            kind: ObjectKind::Ordinary,
+            data: ObjectData::Ordinary,
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
         };
 
         object.set_internal_slot("extensible", Value::from(true));
@@ -348,14 +330,13 @@ impl Object {
     }
 
     /// Return a new ObjectData struct, with `kind` set to Ordinary
-    pub fn function() -> Self {
+    pub fn function(function: Function) -> Self {
         let mut object = Self {
-            kind: ObjectKind::Function,
+            data: ObjectData::Function(function),
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
         };
 
         object.set_internal_slot("extensible", Value::from(true));
@@ -378,73 +359,48 @@ impl Object {
         obj
     }
 
-    /// Set the function this object wraps
-    pub fn set_func(&mut self, val: Function) {
-        self.func = Some(val);
-    }
-
     /// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
-    fn from_boolean(argument: &Value) -> Self {
-        let mut obj = Self {
-            kind: ObjectKind::Boolean,
+    pub fn boolean(value: bool) -> Self {
+        Self {
+            data: ObjectData::Boolean(value),
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
-        };
-
-        obj.internal_slots
-            .insert("BooleanData".to_string(), argument.clone());
-        obj
+        }
     }
 
     /// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
-    fn from_number(argument: &Value) -> Self {
-        let mut obj = Self {
-            kind: ObjectKind::Number,
+    pub fn number(value: f64) -> Self {
+        Self {
+            data: ObjectData::Number(value),
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
-        };
-
-        obj.internal_slots
-            .insert("NumberData".to_string(), argument.clone());
-        obj
+        }
     }
 
     /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
-    fn from_string(argument: &Value) -> Self {
-        let mut obj = Self {
-            kind: ObjectKind::String,
+    pub fn string(value: String) -> Self {
+        Self {
+            data: ObjectData::String(value),
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
-        };
-
-        obj.internal_slots
-            .insert("StringData".to_string(), argument.clone());
-        obj
+        }
     }
 
     /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
-    fn from_bigint(argument: &Value) -> Self {
-        let mut obj = Self {
-            kind: ObjectKind::BigInt,
+    pub fn bigint(value: AstBigInt) -> Self {
+        Self {
+            data: ObjectData::BigInt(value),
             internal_slots: FxHashMap::default(),
             properties: FxHashMap::default(),
             sym_properties: FxHashMap::default(),
             state: None,
-            func: None,
-        };
-
-        obj.internal_slots
-            .insert("BigIntData".to_string(), argument.clone());
-        obj
+        }
     }
 
     /// Converts the `Value` to an `Object` type.
@@ -454,11 +410,12 @@ impl Object {
     ///
     /// [spec]: https://tc39.es/ecma262/#sec-toobject
     pub fn from(value: &Value) -> Result<Self, ()> {
-        match *value.deref().borrow() {
-            ValueData::Boolean(_) => Ok(Self::from_boolean(value)),
-            ValueData::Rational(_) => Ok(Self::from_number(value)),
-            ValueData::String(_) => Ok(Self::from_string(value)),
-            ValueData::BigInt(_) => Ok(Self::from_bigint(value)),
+        match *value.data() {
+            ValueData::Boolean(a) => Ok(Self::boolean(a)),
+            ValueData::Rational(a) => Ok(Self::number(a)),
+            ValueData::Integer(a) => Ok(Self::number(f64::from(a))),
+            ValueData::String(ref a) => Ok(Self::string(a.clone())),
+            ValueData::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())),
             ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()),
             _ => Err(()),
         }
@@ -471,9 +428,9 @@ impl Object {
     ///
     /// [spec]: https://tc39.es/ecma262/#sec-iscallable
     pub fn is_callable(&self) -> bool {
-        match self.func {
-            Some(ref function) => function.is_callable(),
-            None => false,
+        match self.data {
+            ObjectData::Function(ref function) => function.is_callable(),
+            _ => false,
         }
     }
 
@@ -484,59 +441,47 @@ impl Object {
     ///
     /// [spec]: https://tc39.es/ecma262/#sec-isconstructor
     pub fn is_constructable(&self) -> bool {
-        match self.func {
-            Some(ref function) => function.is_constructable(),
-            None => false,
+        match self.data {
+            ObjectData::Function(ref function) => function.is_constructable(),
+            _ => false,
         }
     }
 }
 
 /// Defines the different types of objects.
-#[derive(Finalize, Debug, Copy, Clone, Eq, PartialEq)]
-pub enum ObjectKind {
-    Function,
+#[derive(Debug, Trace, Finalize, Clone)]
+pub enum ObjectData {
+    Function(Function),
     Array,
-    String,
+    String(String),
     Symbol,
     Error,
+    Boolean(bool),
+    Number(f64),
+    BigInt(AstBigInt),
     Ordinary,
-    Boolean,
-    Number,
-    BigInt,
 }
 
-impl Display for ObjectKind {
+impl Display for ObjectData {
     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
         write!(
             f,
             "{}",
             match self {
-                Self::Function => "Function",
+                Self::Function(_) => "Function",
                 Self::Array => "Array",
-                Self::String => "String",
+                Self::String(_) => "String",
                 Self::Symbol => "Symbol",
                 Self::Error => "Error",
                 Self::Ordinary => "Ordinary",
-                Self::Boolean => "Boolean",
-                Self::Number => "Number",
-                Self::BigInt => "BigInt",
+                Self::Boolean(_) => "Boolean",
+                Self::Number(_) => "Number",
+                Self::BigInt(_) => "BigInt",
             }
         )
     }
 }
 
-/// `Trace` implementation for `ObjectKind`.
-///
-/// This is indeed safe, but we need to mark this as an empty trace because neither
-// `NativeFunctionData` nor Node hold any GC'd objects, but Gc doesn't know that. So we need to
-/// signal it manually. `rust-gc` does not have a `Trace` implementation for `fn(_, _, _)`.
-///
-/// <https://github.com/Manishearth/rust-gc/blob/master/gc/src/trace.rs>
-/// Waiting on <https://github.com/Manishearth/rust-gc/issues/87> until we can derive Copy
-unsafe impl Trace for ObjectKind {
-    unsafe_empty_trace!();
-}
-
 /// Create a new object.
 pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
     if let Some(arg) = args.get(0) {
diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs
index af82d3bfe80..4d333f90482 100644
--- a/boa/src/builtins/regexp/mod.rs
+++ b/boa/src/builtins/regexp/mod.rs
@@ -16,7 +16,7 @@ use regex::Regex;
 use super::function::{make_builtin_fn, make_constructor_fn};
 use crate::{
     builtins::{
-        object::{InternalState, ObjectKind},
+        object::{InternalState, ObjectData},
         property::Property,
         value::{ResultValue, Value, ValueData},
     },
@@ -159,7 +159,7 @@ impl RegExp {
 
         // This value is used by console.log and other routines to match Object type
         // to its Javascript Identifier (global constructor method name)
-        this.set_kind(ObjectKind::Ordinary);
+        this.set_data(ObjectData::Ordinary);
         this.set_internal_slot("RegExpMatcher", Value::undefined());
         this.set_internal_slot("OriginalSource", Value::from(regex_body));
         this.set_internal_slot("OriginalFlags", Value::from(regex_flags));
@@ -462,7 +462,7 @@ impl RegExp {
         let length = matches.len();
         let result = Value::from(matches);
         result.set_field("length", Value::from(length));
-        result.set_kind(ObjectKind::Array);
+        result.set_data(ObjectData::Array);
 
         Ok(result)
     }
diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs
index 06ba55460b1..dee862feb6b 100644
--- a/boa/src/builtins/string/mod.rs
+++ b/boa/src/builtins/string/mod.rs
@@ -15,7 +15,7 @@ mod tests;
 use super::function::{make_builtin_fn, make_constructor_fn};
 use crate::{
     builtins::{
-        object::{Object, ObjectKind},
+        object::{Object, ObjectData},
         property::Property,
         value::{ResultValue, Value, ValueData},
         RegExp,
@@ -46,32 +46,20 @@ impl String {
     ) -> ResultValue {
         // This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
         // to its Javascript Identifier (global constructor method name)
-        let s = args.get(0).unwrap_or(&Value::string("")).clone();
-        let length_str = s.to_string().chars().count();
+        let string = args.get(0).map(|x| x.to_string()).unwrap_or_default();
+        let length = string.chars().count();
 
-        this.set_field("length", Value::from(length_str as i32));
+        this.set_field("length", Value::from(length as i32));
 
-        this.set_kind(ObjectKind::String);
-        this.set_internal_slot("StringData", s);
+        this.set_data(ObjectData::String(string.clone()));
 
-        let arg = match args.get(0) {
-            Some(v) => v.clone(),
-            None => Value::undefined(),
-        };
-
-        if arg.is_undefined() {
-            return Ok("".into());
-        }
-
-        Ok(Value::from(arg.to_string()))
+        Ok(Value::from(string))
     }
 
     /// Get the string value to a primitive string
     #[allow(clippy::wrong_self_convention)]
     pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
-        // Get String from String Object and send it back as a new value
-        let primitive_val = this.get_internal_slot("StringData");
-        Ok(Value::from(format!("{}", primitive_val)))
+        Ok(Value::from(this.to_string()))
     }
 
     /// `String.prototype.charAt( index )`
diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs
index e71a5dc09c3..9fd53fae84b 100644
--- a/boa/src/builtins/symbol/mod.rs
+++ b/boa/src/builtins/symbol/mod.rs
@@ -22,7 +22,7 @@ use super::function::{make_builtin_fn, make_constructor_fn};
 use crate::{
     builtins::{
         object::{
-            internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
+            internal_methods_trait::ObjectInternalMethods, Object, ObjectData, INSTANCE_PROTOTYPE,
             PROTOTYPE,
         },
         value::{ResultValue, Value, ValueData},
@@ -48,7 +48,7 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
     // So we start by creating an Object
     // TODO: Set prototype to Symbol.prototype (by changing to Object::create(), use interpreter to get Symbol.prototype)
     let mut sym_instance = Object::default();
-    sym_instance.kind = ObjectKind::Symbol;
+    sym_instance.data = ObjectData::Symbol;
 
     // Set description which should either be undefined or a string
     let desc_string = match args.get(0) {
diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs
index 0d3f86672cb..c2351973acf 100644
--- a/boa/src/builtins/value/mod.rs
+++ b/boa/src/builtins/value/mod.rs
@@ -9,7 +9,7 @@ use crate::builtins::{
     function::Function,
     object::{
         internal_methods_trait::ObjectInternalMethods, InternalState, InternalStateCell, Object,
-        ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE,
+        ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE,
     },
     property::Property,
 };
@@ -133,9 +133,9 @@ impl Value {
     }
 
     /// Similar to `new_object`, but you can pass a prototype to create from, plus a kind
-    pub fn new_object_from_prototype(proto: Value, kind: ObjectKind) -> Self {
+    pub fn new_object_from_prototype(proto: Value, data: ObjectData) -> Self {
         let mut object = Object::default();
-        object.kind = kind;
+        object.data = data;
 
         object
             .internal_slots
@@ -364,8 +364,8 @@ impl ValueData {
             ValueData::BigInt(b) => Some(b.clone()),
             ValueData::Object(ref o) => {
                 let object = (o).deref().borrow();
-                if object.kind == ObjectKind::BigInt {
-                    object.get_internal_slot("BigIntData").to_bigint()
+                if let ObjectData::BigInt(ref bigint) = object.data {
+                    Some(bigint.clone())
                 } else {
                     None
                 }
@@ -374,6 +374,20 @@ impl ValueData {
         }
     }
 
+    /// Creates a new boolean value from the input
+    pub fn to_boolean(&self) -> bool {
+        match *self {
+            Self::Undefined | Self::Null => false,
+            Self::Symbol(_) | Self::Object(_) => true,
+            Self::String(ref s) if !s.is_empty() => true,
+            Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
+            Self::Integer(n) if n != 0 => true,
+            Self::BigInt(ref n) if *n != 0 => true,
+            Self::Boolean(v) => v,
+            _ => false,
+        }
+    }
+
     pub fn as_object(&self) -> Option<GcCellRef<'_, Object>> {
         match *self {
             ValueData::Object(ref o) => Some(o.borrow()),
@@ -600,7 +614,7 @@ impl ValueData {
         let val = val.into();
 
         if let Self::Object(ref obj) = *self {
-            if obj.borrow().kind == ObjectKind::Array {
+            if let ObjectData::Array = obj.borrow().data {
                 if let Ok(num) = field.to_string().parse::<usize>() {
                     if num > 0 {
                         let len = i32::from(&self.get_field("length"));
@@ -634,9 +648,9 @@ impl ValueData {
     }
 
     /// Set the kind of an object
-    pub fn set_kind(&self, kind: ObjectKind) {
+    pub fn set_data(&self, data: ObjectData) {
         if let Self::Object(ref obj) = *self {
-            (*obj.deref().borrow_mut()).kind = kind;
+            (*obj.deref().borrow_mut()).data = data;
         }
     }
 
@@ -663,13 +677,11 @@ impl ValueData {
     }
 
     /// Consume the function and return a Value
-    pub fn from_func(native_func: Function) -> Value {
-        // Object with Kind set to function
-        let mut new_func = crate::builtins::object::Object::function();
+    pub fn from_func(function: Function) -> Value {
         // Get Length
-        let length = native_func.params.len();
-        // Set [[Call]] internal slot
-        new_func.set_func(native_func);
+        let length = function.params.len();
+        // Object with Kind set to function
+        let new_func = Object::function(function);
         // Wrap Object in GC'd Value
         let new_func_val = Value::from(new_func);
         // Set length to parameters
@@ -836,19 +848,10 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
         ValueData::Object(ref v) => {
             // Can use the private "type" field of an Object to match on
             // which type of Object it represents for special printing
-            match v.borrow().kind {
-                ObjectKind::String => String::from(
-                    v.borrow()
-                        .internal_slots
-                        .get("StringData")
-                        .expect("Cannot get primitive value from String"),
-                ),
-                ObjectKind::Boolean => {
-                    let bool_data = v.borrow().get_internal_slot("BooleanData").to_string();
-
-                    format!("Boolean {{ {} }}", bool_data)
-                }
-                ObjectKind::Array => {
+            match v.borrow().data {
+                ObjectData::String(ref string) => string.clone(),
+                ObjectData::Boolean(boolean) => format!("Boolean {{ {} }}", boolean),
+                ObjectData::Array => {
                     let len = i32::from(
                         &v.borrow()
                             .properties
diff --git a/boa/src/exec/expression/mod.rs b/boa/src/exec/expression/mod.rs
index 57fc661ca7a..e146e96f8b3 100644
--- a/boa/src/exec/expression/mod.rs
+++ b/boa/src/exec/expression/mod.rs
@@ -3,7 +3,7 @@
 use super::{Executable, Interpreter};
 use crate::{
     builtins::{
-        object::{INSTANCE_PROTOTYPE, PROTOTYPE},
+        object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
         value::{ResultValue, Value, ValueData},
     },
     syntax::ast::node::{Call, New, Node},
@@ -69,12 +69,13 @@ impl Executable for New {
         this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE));
 
         match func_object.data() {
-            ValueData::Object(ref o) => o.clone().borrow_mut().func.as_ref().unwrap().construct(
-                &mut func_object.clone(),
-                &v_args,
-                interpreter,
-                &mut this,
-            ),
+            ValueData::Object(ref obj) => {
+                let obj = (**obj).borrow();
+                if let ObjectData::Function(ref func) = obj.data {
+                    return func.construct(func_object.clone(), &mut this, &v_args, interpreter);
+                }
+                panic!("TypeError: not a function");
+            }
             _ => Ok(Value::undefined()),
         }
     }
diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs
index 4f0c8266818..4bfb139ccfc 100644
--- a/boa/src/exec/mod.rs
+++ b/boa/src/exec/mod.rs
@@ -15,7 +15,7 @@ use crate::{
     builtins::{
         function::{Function as FunctionObject, FunctionBody, ThisMode},
         object::{
-            internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
+            internal_methods_trait::ObjectInternalMethods, Object, ObjectData, INSTANCE_PROTOTYPE,
             PROTOTYPE,
         },
         property::Property,
@@ -97,8 +97,8 @@ impl Interpreter {
             callable,
         );
 
-        let mut new_func = Object::function();
-        new_func.set_func(func);
+        let new_func = Object::function(func);
+
         let val = Value::from(new_func);
         val.set_internal_slot(INSTANCE_PROTOTYPE, function_prototype.clone());
         val.set_field(PROTOTYPE, proto);
@@ -122,8 +122,10 @@ impl Interpreter {
         match *f.data() {
             ValueData::Object(ref obj) => {
                 let obj = (**obj).borrow();
-                let func = obj.func.as_ref().expect("Expected function");
-                func.call(&mut f.clone(), arguments_list, self, this)
+                if let ObjectData::Function(ref func) = obj.data {
+                    return func.call(f.clone(), this, arguments_list, self);
+                }
+                panic!("TypeError: not a function");
             }
             _ => Err(Value::undefined()),
         }
@@ -151,7 +153,7 @@ impl Interpreter {
     pub(crate) fn extract_array_properties(&mut self, value: &Value) -> Result<Vec<Value>, ()> {
         if let ValueData::Object(ref x) = *value.deref().borrow() {
             // Check if object is array
-            if x.deref().borrow().kind == ObjectKind::Array {
+            if let ObjectData::Array = x.deref().borrow().data {
                 let length: i32 = self.value_to_rust_number(&value.get_field("length")) as i32;
                 let values: Vec<Value> = (0..length)
                     .map(|idx| value.get_field(idx.to_string()))
@@ -281,46 +283,51 @@ impl Interpreter {
             ValueData::Undefined | ValueData::Integer(_) | ValueData::Null => {
                 Err(Value::undefined())
             }
-            ValueData::Boolean(_) => {
+            ValueData::Boolean(boolean) => {
                 let proto = self
                     .realm
                     .environment
                     .get_binding_value("Boolean")
                     .get_field(PROTOTYPE);
 
-                let bool_obj = Value::new_object_from_prototype(proto, ObjectKind::Boolean);
-                bool_obj.set_internal_slot("BooleanData", value.clone());
-                Ok(bool_obj)
+                Ok(Value::new_object_from_prototype(
+                    proto,
+                    ObjectData::Boolean(boolean),
+                ))
             }
-            ValueData::Rational(_) => {
+            ValueData::Rational(rational) => {
                 let proto = self
                     .realm
                     .environment
                     .get_binding_value("Number")
                     .get_field(PROTOTYPE);
-                let number_obj = Value::new_object_from_prototype(proto, ObjectKind::Number);
-                number_obj.set_internal_slot("NumberData", value.clone());
-                Ok(number_obj)
+
+                Ok(Value::new_object_from_prototype(
+                    proto,
+                    ObjectData::Number(rational),
+                ))
             }
-            ValueData::String(_) => {
+            ValueData::String(ref string) => {
                 let proto = self
                     .realm
                     .environment
                     .get_binding_value("String")
                     .get_field(PROTOTYPE);
-                let string_obj = Value::new_object_from_prototype(proto, ObjectKind::String);
-                string_obj.set_internal_slot("StringData", value.clone());
-                Ok(string_obj)
+
+                Ok(Value::new_object_from_prototype(
+                    proto,
+                    ObjectData::String(string.clone()),
+                ))
             }
             ValueData::Object(_) | ValueData::Symbol(_) => Ok(value.clone()),
-            ValueData::BigInt(_) => {
+            ValueData::BigInt(ref bigint) => {
                 let proto = self
                     .realm
                     .environment
                     .get_binding_value("BigInt")
                     .get_field(PROTOTYPE);
-                let bigint_obj = Value::new_object_from_prototype(proto, ObjectKind::BigInt);
-                bigint_obj.set_internal_slot("BigIntData", value.clone());
+                let bigint_obj =
+                    Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone()));
                 Ok(bigint_obj)
             }
         }