public JsValue CreateValue(Type targetType, JavaScriptValueSafeHandle valueHandle)
        {
            if (targetType == null)
            {
                throw new ArgumentNullException(nameof(targetType));
            }

            if (targetType.IsSubclassOf(typeof(JsValue)) == false)
            {
                throw new ArgumentOutOfRangeException(nameof(targetType));
            }

            if (valueHandle == JavaScriptValueSafeHandle.Invalid)
            {
                return(null);
            }

            return(m_valuePool.GetOrAdd(valueHandle, () =>
            {
                JsValue result;

                if (typeof(JsObject).IsSameOrSubclass(targetType))
                {
                    result = Activator.CreateInstance(targetType, new object[] { m_engine, Context, valueHandle }) as JsValue;
                }
                else if (typeof(JsExternalObject).IsSameOrSubclass(targetType))
                {
                    //TODO: This isn't exactly true, we should set the ExternalData to the GCHandle.
                    //Then we can new JsExternalObject(m_engine, Context, valueHandle, Engine.GetExternalData(valueHandle));
                    throw new InvalidOperationException("External Objects must first be created by ...");
                }
                else if (typeof(JsBoolean) == targetType)
                {
                    result = new JsBoolean(m_engine, Context, valueHandle);
                }
                else if (typeof(JsNumber) == targetType)
                {
                    result = new JsNumber(m_engine, Context, valueHandle);
                }
                else if (typeof(JsString) == targetType)
                {
                    result = new JsString(m_engine, Context, valueHandle);
                }
                else if (typeof(JsSymbol) == targetType)
                {
                    result = new JsSymbol(m_engine, Context, valueHandle);
                }
                else if (typeof(JsUndefined) == targetType)
                {
                    result = new JsUndefined(m_engine, Context, valueHandle);
                }
                else if (typeof(JsNull) == targetType)
                {
                    result = new JsNull(m_engine, Context, valueHandle);
                }
                else
                {
                    throw new InvalidOperationException($"Type {targetType} must subclass JsValue");
                }

                if (result == null)
                {
                    throw new ArgumentOutOfRangeException(nameof(targetType), $"Unable to create an object of type {targetType}.");
                }

                result.BeforeCollect += JsValueBeforeCollectCallback;
                return result;
            }));
        }
        /// <summary>
        /// Returns a new JavaScriptValue for the specified handle querying for the handle's value type.
        /// </summary>
        /// <remarks>
        /// Use the valueType parameter carefully. If the resulting type does not match the handle type unexpected issues may occur.
        /// </remarks>
        /// <returns>The JavaScript Value that represents the handle</returns>
        public JsValue CreateValue(JavaScriptValueSafeHandle valueHandle, JsValueType?valueType = null)
        {
            if (valueHandle == JavaScriptValueSafeHandle.Invalid)
            {
                return(null);
            }

            return(m_valuePool.GetOrAdd(valueHandle, () =>
            {
                if (valueType.HasValue == false)
                {
                    valueType = m_engine.JsGetValueType(valueHandle);
                }

                JsValue result;
                switch (valueType.Value)
                {
                case JsValueType.Array:
                    result = new JsArray(m_engine, Context, valueHandle);
                    break;

                case JsValueType.ArrayBuffer:
                    result = new JsArrayBuffer(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Boolean:
                    result = new JsBoolean(m_engine, Context, valueHandle);
                    break;

                case JsValueType.DataView:
                    result = new JsDataView(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Error:
                    result = new JsError(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Function:
                    result = new JsFunction(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Null:
                    result = new JsNull(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Number:
                    result = new JsNumber(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Object:
                    result = new JsObject(m_engine, Context, valueHandle);
                    break;

                case JsValueType.String:
                    result = new JsString(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Symbol:
                    result = new JsSymbol(m_engine, Context, valueHandle);
                    break;

                case JsValueType.TypedArray:
                    result = new JsTypedArray(m_engine, Context, valueHandle);
                    break;

                case JsValueType.Undefined:
                    result = new JsUndefined(m_engine, Context, valueHandle);
                    break;

                default:
                    throw new NotImplementedException($"Error Creating JavaScript Value: The JavaScript Value Type '{valueType}' is unknown, invalid, or has not been implemented.");
                }

                result.BeforeCollect += JsValueBeforeCollectCallback;

                return result;
            }));
        }