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_();
55 return RefType::exn();
56 case RefType::TypeRef
:
57 switch (typeDef()->kind()) {
58 case TypeDefKind::Array
:
59 case TypeDefKind::Struct
:
60 return RefType::any();
61 case TypeDefKind::Func
:
62 return RefType::func();
63 case TypeDefKind::None
:
64 MOZ_CRASH("should not see TypeDefKind::None at this point");
67 MOZ_CRASH("switch is exhaustive");
70 TypeDefKind
RefType::typeDefKind() const {
73 return TypeDefKind::Struct
;
75 return TypeDefKind::Array
;
77 return TypeDefKind::Func
;
79 return TypeDefKind::None
;
81 MOZ_CRASH("switch is exhaustive");
84 static bool ToRefType(JSContext
* cx
, JSLinearString
* typeLinearStr
,
86 if (StringEqualsLiteral(typeLinearStr
, "anyfunc") ||
87 StringEqualsLiteral(typeLinearStr
, "funcref")) {
88 // The JS API uses "anyfunc" uniformly as the external name of funcref. We
89 // also allow "funcref" for compatibility with code we've already shipped.
90 *out
= RefType::func();
93 if (StringEqualsLiteral(typeLinearStr
, "externref")) {
94 *out
= RefType::extern_();
97 #ifdef ENABLE_WASM_EXNREF
98 if (ExnRefAvailable(cx
)) {
99 if (StringEqualsLiteral(typeLinearStr
, "exnref")) {
100 *out
= RefType::exn();
105 #ifdef ENABLE_WASM_GC
106 if (GcAvailable(cx
)) {
107 if (StringEqualsLiteral(typeLinearStr
, "anyref")) {
108 *out
= RefType::any();
111 if (StringEqualsLiteral(typeLinearStr
, "eqref")) {
112 *out
= RefType::eq();
115 if (StringEqualsLiteral(typeLinearStr
, "i31ref")) {
116 *out
= RefType::i31();
119 if (StringEqualsLiteral(typeLinearStr
, "structref")) {
120 *out
= RefType::struct_();
123 if (StringEqualsLiteral(typeLinearStr
, "arrayref")) {
124 *out
= RefType::array();
127 if (StringEqualsLiteral(typeLinearStr
, "nullfuncref")) {
128 *out
= RefType::nofunc();
131 if (StringEqualsLiteral(typeLinearStr
, "nullexternref")) {
132 *out
= RefType::noextern();
135 if (StringEqualsLiteral(typeLinearStr
, "nullref")) {
136 *out
= RefType::none();
142 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
143 JSMSG_WASM_BAD_STRING_VAL_TYPE
);
147 enum class RefTypeResult
{
153 static RefTypeResult
MaybeToRefType(JSContext
* cx
, HandleObject obj
,
155 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
156 if (!wasm::FunctionReferencesAvailable(cx
)) {
157 return RefTypeResult::Unparsed
;
160 JSAtom
* refAtom
= Atomize(cx
, "ref", strlen("ref"));
162 return RefTypeResult::Failure
;
164 RootedId
refId(cx
, AtomToId(refAtom
));
166 RootedValue
refVal(cx
);
167 if (!GetProperty(cx
, obj
, obj
, refId
, &refVal
)) {
168 return RefTypeResult::Failure
;
171 RootedString
typeStr(cx
, ToString(cx
, refVal
));
173 return RefTypeResult::Failure
;
176 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
177 if (!typeLinearStr
) {
178 return RefTypeResult::Failure
;
181 if (StringEqualsLiteral(typeLinearStr
, "func")) {
182 *out
= RefType::func();
183 } else if (StringEqualsLiteral(typeLinearStr
, "extern")) {
184 *out
= RefType::extern_();
185 # ifdef ENABLE_WASM_EXNREF
186 } else if (ExnRefAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "exn")) {
187 *out
= RefType::exn();
189 # ifdef ENABLE_WASM_GC
190 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "any")) {
191 *out
= RefType::any();
192 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "eq")) {
193 *out
= RefType::eq();
194 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "i31")) {
195 *out
= RefType::i31();
196 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "struct")) {
197 *out
= RefType::struct_();
198 } else if (GcAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "array")) {
199 *out
= RefType::array();
202 return RefTypeResult::Unparsed
;
205 JSAtom
* nullableAtom
= Atomize(cx
, "nullable", strlen("nullable"));
207 return RefTypeResult::Failure
;
209 RootedId
nullableId(cx
, AtomToId(nullableAtom
));
210 RootedValue
nullableVal(cx
);
211 if (!GetProperty(cx
, obj
, obj
, nullableId
, &nullableVal
)) {
212 return RefTypeResult::Failure
;
215 bool nullable
= ToBoolean(nullableVal
);
217 *out
= out
->asNonNullable();
219 MOZ_ASSERT(out
->isNullable() == nullable
);
220 return RefTypeResult::Parsed
;
222 return RefTypeResult::Unparsed
;
226 bool wasm::ToValType(JSContext
* cx
, HandleValue v
, ValType
* out
) {
228 RootedObject
obj(cx
, &v
.toObject());
230 switch (MaybeToRefType(cx
, obj
, &refType
)) {
231 case RefTypeResult::Failure
:
233 case RefTypeResult::Parsed
:
234 *out
= ValType(refType
);
236 case RefTypeResult::Unparsed
:
241 RootedString
typeStr(cx
, ToString(cx
, v
));
246 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
247 if (!typeLinearStr
) {
251 if (StringEqualsLiteral(typeLinearStr
, "i32")) {
253 } else if (StringEqualsLiteral(typeLinearStr
, "i64")) {
255 } else if (StringEqualsLiteral(typeLinearStr
, "f32")) {
257 } else if (StringEqualsLiteral(typeLinearStr
, "f64")) {
259 #ifdef ENABLE_WASM_SIMD
260 } else if (SimdAvailable(cx
) && StringEqualsLiteral(typeLinearStr
, "v128")) {
261 *out
= ValType::V128
;
265 if (ToRefType(cx
, typeLinearStr
, &rt
)) {
268 // ToRefType will report an error when it fails, just return false
276 bool wasm::ToRefType(JSContext
* cx
, HandleValue v
, RefType
* out
) {
278 RootedObject
obj(cx
, &v
.toObject());
279 switch (MaybeToRefType(cx
, obj
, out
)) {
280 case RefTypeResult::Failure
:
282 case RefTypeResult::Parsed
:
284 case RefTypeResult::Unparsed
:
289 RootedString
typeStr(cx
, ToString(cx
, v
));
294 Rooted
<JSLinearString
*> typeLinearStr(cx
, typeStr
->ensureLinear(cx
));
295 if (!typeLinearStr
) {
299 return ToRefType(cx
, typeLinearStr
, out
);
302 UniqueChars
wasm::ToString(RefType type
, const TypeContext
* types
) {
303 // Try to emit a shorthand version first
304 if (type
.isNullable() && !type
.isTypeRef()) {
305 const char* literal
= nullptr;
306 switch (type
.kind()) {
310 case RefType::Extern
:
311 literal
= "externref";
319 case RefType::NoFunc
:
320 literal
= "nullfuncref";
322 case RefType::NoExtern
:
323 literal
= "nullexternref";
334 case RefType::Struct
:
335 literal
= "structref";
338 literal
= "arrayref";
340 case RefType::TypeRef
: {
341 MOZ_CRASH("type ref should not be possible here");
344 return DuplicateString(literal
);
347 // Emit the full reference type with heap type
348 const char* heapType
= nullptr;
349 switch (type
.kind()) {
353 case RefType::Extern
:
362 case RefType::NoFunc
:
365 case RefType::NoExtern
:
366 heapType
= "noextern";
377 case RefType::Struct
:
383 case RefType::TypeRef
: {
385 uint32_t typeIndex
= types
->indexOf(*type
.typeDef());
386 return JS_smprintf("(ref %s%d)", type
.isNullable() ? "null " : "",
389 return JS_smprintf("(ref %s?)", type
.isNullable() ? "null " : "");
392 return JS_smprintf("(ref %s%s)", type
.isNullable() ? "null " : "", heapType
);
395 UniqueChars
wasm::ToString(ValType type
, const TypeContext
* types
) {
396 return ToString(type
.storageType(), types
);
399 UniqueChars
wasm::ToString(StorageType type
, const TypeContext
* types
) {
400 const char* literal
= nullptr;
401 switch (type
.kind()) {
402 case StorageType::I8
:
405 case StorageType::I16
:
408 case StorageType::I32
:
411 case StorageType::I64
:
414 case StorageType::V128
:
417 case StorageType::F32
:
420 case StorageType::F64
:
423 case StorageType::Ref
:
424 return ToString(type
.refType(), types
);
426 return DuplicateString(literal
);
429 UniqueChars
wasm::ToString(const Maybe
<ValType
>& type
,
430 const TypeContext
* types
) {
431 return type
? ToString(type
.ref(), types
) : JS_smprintf("%s", "void");