e53ebc741a9c8c7d65ead129937b671a5d96fe26
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSCast.h
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include "JSCell.h"
29
30 namespace JSC {
31
32 template<typename To, typename From>
33 inline To jsCast(From* from)
34 {
35     static_assert(std::is_base_of<JSCell, typename std::remove_pointer<To>::type>::value && std::is_base_of<JSCell, typename std::remove_pointer<From>::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell");
36     ASSERT_WITH_SECURITY_IMPLICATION(!from || from->JSCell::inherits(*from->JSCell::vm(), std::remove_pointer<To>::type::info()));
37     return static_cast<To>(from);
38 }
39
40 template<typename To>
41 inline To jsCast(JSValue from)
42 {
43     static_assert(std::is_base_of<JSCell, typename std::remove_pointer<To>::type>::value, "JS casting expects that the types you are casting to is a subclass of JSCell");
44     ASSERT_WITH_SECURITY_IMPLICATION(from.isCell() && from.asCell()->JSCell::inherits(*from.asCell()->vm(), std::remove_pointer<To>::type::info()));
45     return static_cast<To>(from.asCell());
46 }
47
48 // Specific type overloads.
49 #define FOR_EACH_JS_DYNAMIC_CAST_JS_TYPE_OVERLOAD(macro) \
50     macro(JSFixedArray, JSType::JSFixedArrayType, JSType::JSFixedArrayType) \
51     macro(JSObject, FirstObjectType, LastObjectType) \
52     macro(JSFinalObject, JSType::FinalObjectType, JSType::FinalObjectType) \
53     macro(JSFunction, JSType::JSFunctionType, JSType::JSFunctionType) \
54     macro(InternalFunction, JSType::InternalFunctionType, JSType::InternalFunctionType) \
55     macro(JSArray, JSType::ArrayType, JSType::DerivedArrayType) \
56     macro(JSArrayBufferView, FirstTypedArrayType, LastTypedArrayType) \
57     macro(JSSet, JSType::JSSetType, JSType::JSSetType) \
58     macro(JSMap, JSType::JSMapType, JSType::JSMapType) \
59     macro(JSWeakSet, JSType::JSWeakSetType, JSType::JSWeakSetType) \
60     macro(JSWeakMap, JSType::JSWeakMapType, JSType::JSWeakMapType) \
61     macro(NumberObject, JSType::NumberObjectType, JSType::NumberObjectType) \
62     macro(ProxyObject, JSType::ProxyObjectType, JSType::ProxyObjectType) \
63     macro(RegExpObject, JSType::RegExpObjectType, JSType::RegExpObjectType) \
64     macro(WebAssemblyToJSCallee, JSType::WebAssemblyToJSCalleeType, JSType::WebAssemblyToJSCalleeType) \
65     macro(DirectArguments, JSType::DirectArgumentsType, JSType::DirectArgumentsType) \
66     macro(ScopedArguments, JSType::ScopedArgumentsType, JSType::ScopedArgumentsType) \
67     macro(ClonedArguments, JSType::ClonedArgumentsType, JSType::ClonedArgumentsType) \
68     macro(JSGlobalObject, JSType::GlobalObjectType, JSType::GlobalObjectType) \
69     macro(JSGlobalLexicalEnvironment, JSType::GlobalLexicalEnvironmentType, JSType::GlobalLexicalEnvironmentType) \
70     macro(JSSegmentedVariableObject, JSType::GlobalObjectType, JSType::GlobalLexicalEnvironmentType) \
71     macro(JSModuleEnvironment, JSType::ModuleEnvironmentType, JSType::ModuleEnvironmentType) \
72     macro(JSLexicalEnvironment, JSType::LexicalEnvironmentType, JSType::ModuleEnvironmentType) \
73     macro(JSSymbolTableObject, JSType::GlobalObjectType, JSType::ModuleEnvironmentType) \
74     macro(JSScope, JSType::GlobalObjectType, JSType::WithScopeType) \
75
76
77 // Forward declare the classes because they may not already exist.
78 #define FORWARD_DECLARE_OVERLOAD_CLASS(className, jsType, op) class className;
79 FOR_EACH_JS_DYNAMIC_CAST_JS_TYPE_OVERLOAD(FORWARD_DECLARE_OVERLOAD_CLASS)
80 #undef FORWARD_DECLARE_OVERLOAD_CLASS
81
82 namespace JSCastingHelpers {
83
84 template<bool isFinal>
85 struct FinalTypeDispatcher {
86     template<typename Target, typename From>
87     static inline bool inheritsGeneric(VM& vm, From* from)
88     {
89         static_assert(!std::is_same<JSObject*, Target*>::value, "This ensures our overloads work");
90         static_assert(std::is_base_of<JSCell, Target>::value && std::is_base_of<JSCell, typename std::remove_pointer<From>::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell");
91         // Do not use inherits<Target>(vm) since inherits<T> depends on this function.
92         return from->JSCell::inherits(vm, Target::info());
93     }
94 };
95
96 template<>
97 struct FinalTypeDispatcher</* isFinal */ true> {
98     template<typename Target, typename From>
99     static inline bool inheritsGeneric(VM& vm, From* from)
100     {
101         static_assert(!std::is_same<JSObject*, Target*>::value, "This ensures our overloads work");
102         static_assert(std::is_base_of<JSCell, Target>::value && std::is_base_of<JSCell, typename std::remove_pointer<From>::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell");
103         static_assert(std::is_final<Target>::value, "Target is a final type");
104         bool canCast = from->JSCell::classInfo(vm) == Target::info();
105         // Do not use inherits<Target>(vm) since inherits<T> depends on this function.
106         ASSERT_UNUSED(vm, canCast == from->JSCell::inherits(vm, Target::info()));
107         return canCast;
108     }
109 };
110
111 template<typename Target, typename From>
112 inline bool inheritsJSTypeImpl(VM& vm, From* from, JSType firstType, JSType lastType)
113 {
114     static_assert(std::is_base_of<JSCell, Target>::value && std::is_base_of<JSCell, typename std::remove_pointer<From>::type>::value, "JS casting expects that the types you are casting to/from are subclasses of JSCell");
115     bool canCast = firstType <= from->type() && from->type() <= lastType;
116     // Do not use inherits<Target>(vm) since inherits<T> depends on this function.
117     ASSERT_UNUSED(vm, canCast == from->JSCell::inherits(vm, Target::info()));
118     return canCast;
119 }
120
121 // C++ has bad syntax so we need to use this struct because C++ doesn't have a
122 // way to say that we are overloading just the first type in a template list...
123 template<typename Target>
124 struct InheritsTraits {
125     template<typename From>
126     static inline bool inherits(VM& vm, From* from) { return FinalTypeDispatcher<std::is_final<Target>::value>::template inheritsGeneric<Target>(vm, from); }
127 };
128
129 #define DEFINE_TRAITS_FOR_JS_TYPE_OVERLOAD(className, firstJSType, lastJSType) \
130     template<> \
131     struct InheritsTraits<className> { \
132         template<typename From> \
133         static inline bool inherits(VM& vm, From* from) { return inheritsJSTypeImpl<className, From>(vm, from, static_cast<JSType>(firstJSType), static_cast<JSType>(lastJSType)); } \
134     }; \
135
136 FOR_EACH_JS_DYNAMIC_CAST_JS_TYPE_OVERLOAD(DEFINE_TRAITS_FOR_JS_TYPE_OVERLOAD)
137
138 #undef DEFINE_TRAITS_FOR_JS_TYPE_OVERLOAD
139
140
141 template<typename Target, typename From>
142 bool inherits(VM& vm, From* from)
143 {
144     using Dispatcher = InheritsTraits<Target>;
145     return Dispatcher::template inherits(vm, from);
146 }
147
148 } // namespace JSCastingHelpers
149
150 template<typename To, typename From>
151 To jsDynamicCast(VM& vm, From* from)
152 {
153     using Dispatcher = JSCastingHelpers::InheritsTraits<typename std::remove_cv<typename std::remove_pointer<To>::type>::type>;
154     if (LIKELY(Dispatcher::template inherits(vm, from)))
155         return static_cast<To>(from);
156     return nullptr;
157 }
158
159 template<typename To>
160 To jsDynamicCast(VM& vm, JSValue from)
161 {
162     if (UNLIKELY(!from.isCell()))
163         return nullptr;
164     return jsDynamicCast<To>(vm, from.asCell());
165 }
166
167 }