1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 * Copyright 2021 Mozilla Foundation
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #include "wasm/WasmValType.h"
21 #include "js/Conversions.h"
22 #include "js/ErrorReport.h"
23 #include "js/friend/ErrorMessages.h" // JSMSG_*
24 #include "js/Printf.h"
27 #include "vm/JSAtomUtils.h" // Atomize
28 #include "vm/JSObject.h"
29 #include "vm/StringType.h"
30 #include "wasm/WasmFeatures.h"
31 #include "wasm/WasmJS.h"
33 #include "vm/JSAtomUtils-inl.h" // AtomToId
34 #include "vm/JSObject-inl.h"
37 using namespace js::wasm
;
39 RefType
RefType::topType() const {
47 return RefType::any();
50 return RefType::func();
52 case RefType::NoExtern
:
53 return RefType::extern_();
54 case RefType::TypeRef
:
55 switch (typeDef()->kind()) {
56 case TypeDefKind::Array
:
57 case TypeDefKind::Struct
:
58 return RefType::any();
59 case TypeDefKind::Func
:
60 return RefType::func();
61 case TypeDefKind::None
:
62 MOZ_CRASH("should not see TypeDefKind::None at this point");
65 MOZ_CRASH("switch is exhaustive");
68 TypeDefKind
RefType::typeDefKind() const {
71 return TypeDefKind::Struct
;
73 return TypeDefKind::Array
;
75 return TypeDefKind::Func
;
77 return TypeDefKind::None
;
79 MOZ_CRASH("switch is exhaustive");
82 static bool ToRefType(JSContext
* cx
, JSLinearString
* typeLinearStr
,
84 if (StringEqualsLiteral(typeLinearStr
, "anyfunc") ||
85 StringEqualsLiteral(typeLinearStr
, "funcref")) {
86 // The JS API uses "anyfunc" uniformly as the external name of funcref. We
87 // also allow "funcref" for compatibility with code we've already shipped.
88 *out
= RefType::func();
91 if (StringEqualsLiteral(typeLinearStr
, "externref")) {
92 *out
= RefType::extern_();
96 if (GcAvailable(cx
)) {
97 if (StringEqualsLiteral(typeLinearStr
, "anyref")) {
98 *out
= RefType::any();
101 if (StringEqualsLiteral(typeLinearStr
, "eqref")) {
102 *out
= RefType::eq();
105 if (StringEqualsLiteral(typeLinearStr
, "i31ref")) {
106 *out
= RefType::i31();
109 if (StringEqualsLiteral(typeLinearStr
, "structref")) {
110 *out
= RefType::struct_();
113 if (StringEqualsLiteral(typeLinearStr
, "arrayref")) {
114 *out
= RefType::array();
117 if (StringEqualsLiteral(typeLinearStr
, "nullfuncref")) {
118 *out
= RefType::nofunc();
121 if (StringEqualsLiteral(typeLinearStr
, "nullexternref")) {
122 *out
= RefType::noextern();
125 if (StringEqualsLiteral(typeLinearStr
, "nullref")) {
126 *out
= RefType::none();
132 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
133 JSMSG_WASM_BAD_STRING_VAL_TYPE
);
137 enum class RefTypeResult
{
143 static RefTypeResult
MaybeToRefType(JSContext
* cx
, HandleObject obj
,
145 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
146 if (!wasm::FunctionReferencesAvailable(cx
)) {
147 return RefTypeResult::Unparsed
;
150 JSAtom
* refAtom
= Atomize(cx
, "ref", strlen("ref"));
152 return RefTypeResult::Failure
;
154 RootedId
refId(cx
, AtomToId(refAtom
));
156 RootedValue
refVal(cx
);
157 if (!GetProperty(cx
, obj
, obj
, refId
, &refVal
)) {
158 return RefTypeResult::Failure
;
161 RootedString
typeStr(cx
, ToString(cx
, refVal
));
163 return RefTypeResult::Failure
;
166 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
167 if (!typeLinearStr
) {
168 return RefTypeResult::Failure
;
171 if (StringEqualsLiteral(typeLinearStr
, "func")) {
172 *out
= RefType::func();
173 } else if (StringEqualsLiteral(typeLinearStr
, "extern")) {
174 *out
= RefType::extern_();
175 # ifdef ENABLE_WASM_GC
176 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "any")) {
177 *out
= RefType::any();
178 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "eq")) {
179 *out
= RefType::eq();
180 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "i31")) {
181 *out
= RefType::i31();
182 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "struct")) {
183 *out
= RefType::struct_();
184 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "array")) {
185 *out
= RefType::array();
188 return RefTypeResult::Unparsed
;
191 JSAtom
* nullableAtom
= Atomize(cx
, "nullable", strlen("nullable"));
193 return RefTypeResult::Failure
;
195 RootedId
nullableId(cx
, AtomToId(nullableAtom
));
196 RootedValue
nullableVal(cx
);
197 if (!GetProperty(cx
, obj
, obj
, nullableId
, &nullableVal
)) {
198 return RefTypeResult::Failure
;
201 bool nullable
= ToBoolean(nullableVal
);
203 *out
= out
->asNonNullable();
205 MOZ_ASSERT(out
->isNullable() == nullable
);
206 return RefTypeResult::Parsed
;
208 return RefTypeResult::Unparsed
;
212 bool wasm::ToValType(JSContext
* cx
, HandleValue v
, ValType
* out
) {
214 RootedObject
obj(cx
, &v
.toObject());
216 switch (MaybeToRefType(cx
, obj
, &refType
)) {
217 case RefTypeResult::Failure
:
219 case RefTypeResult::Parsed
:
220 *out
= ValType(refType
);
222 case RefTypeResult::Unparsed
:
227 RootedString
typeStr(cx
, ToString(cx
, v
));
232 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
233 if (!typeLinearStr
) {
237 if (StringEqualsLiteral(typeLinearStr
, "i32")) {
239 } else if (StringEqualsLiteral(typeLinearStr
, "i64")) {
241 } else if (StringEqualsLiteral(typeLinearStr
, "f32")) {
243 } else if (StringEqualsLiteral(typeLinearStr
, "f64")) {
245 #ifdef ENABLE_WASM_SIMD
246 } else if (SimdAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "v128")) {
247 *out
= ValType::V128
;
251 if (ToRefType(cx
, typeLinearStr
, &rt
)) {
254 // ToRefType will report an error when it fails, just return false
262 bool wasm::ToRefType(JSContext
* cx
, HandleValue v
, RefType
* out
) {
264 RootedObject
obj(cx
, &v
.toObject());
265 switch (MaybeToRefType(cx
, obj
, out
)) {
266 case RefTypeResult::Failure
:
268 case RefTypeResult::Parsed
:
270 case RefTypeResult::Unparsed
:
275 RootedString
typeStr(cx
, ToString(cx
, v
));
280 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
281 if (!typeLinearStr
) {
285 return ToRefType(cx
, typeLinearStr
, out
);
288 UniqueChars
wasm::ToString(RefType type
, const TypeContext
* types
) {
289 // Try to emit a shorthand version first
290 if (type
.isNullable() && !type
.isTypeRef()) {
291 const char* literal
= nullptr;
292 switch (type
.kind()) {
296 case RefType::Extern
:
297 literal
= "externref";
302 case RefType::NoFunc
:
303 literal
= "nullfuncref";
305 case RefType::NoExtern
:
306 literal
= "nullexternref";
317 case RefType::Struct
:
318 literal
= "structref";
321 literal
= "arrayref";
323 case RefType::TypeRef
: {
324 MOZ_CRASH("type ref should not be possible here");
327 return DuplicateString(literal
);
330 // Emit the full reference type with heap type
331 const char* heapType
= nullptr;
332 switch (type
.kind()) {
336 case RefType::Extern
:
342 case RefType::NoFunc
:
345 case RefType::NoExtern
:
346 heapType
= "noextern";
357 case RefType::Struct
:
363 case RefType::TypeRef
: {
365 uint32_t typeIndex
= types
->indexOf(*type
.typeDef());
366 return JS_smprintf("(ref %s%d)", type
.isNullable() ? "null " : "",
369 return JS_smprintf("(ref %s?)", type
.isNullable() ? "null " : "");
372 return JS_smprintf("(ref %s%s)", type
.isNullable() ? "null " : "", heapType
);
375 UniqueChars
wasm::ToString(ValType type
, const TypeContext
* types
) {
376 return ToString(type
.fieldType(), types
);
379 UniqueChars
wasm::ToString(FieldType type
, const TypeContext
* types
) {
380 const char* literal
= nullptr;
381 switch (type
.kind()) {
394 case FieldType::V128
:
404 return ToString(type
.refType(), types
);
406 return DuplicateString(literal
);
409 UniqueChars
wasm::ToString(const Maybe
<ValType
>& type
,
410 const TypeContext
* types
) {
411 return type
? ToString(type
.ref(), types
) : JS_smprintf("%s", "void");