no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / js / src / wasm / WasmValType.cpp
blobd1874b713197b600b055ceeaa73958169b2dff54
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"
25 #include "js/Value.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"
36 using namespace js;
37 using namespace js::wasm;
39 RefType RefType::topType() const {
40 switch (kind()) {
41 case RefType::Any:
42 case RefType::Eq:
43 case RefType::I31:
44 case RefType::Array:
45 case RefType::Struct:
46 case RefType::None:
47 return RefType::any();
48 case RefType::Func:
49 case RefType::NoFunc:
50 return RefType::func();
51 case RefType::Extern:
52 case RefType::NoExtern:
53 return RefType::extern_();
54 case RefType::Exn:
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 {
71 switch (kind()) {
72 case RefType::Struct:
73 return TypeDefKind::Struct;
74 case RefType::Array:
75 return TypeDefKind::Array;
76 case RefType::Func:
77 return TypeDefKind::Func;
78 default:
79 return TypeDefKind::None;
81 MOZ_CRASH("switch is exhaustive");
84 static bool ToRefType(JSContext* cx, JSLinearString* typeLinearStr,
85 RefType* out) {
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();
91 return true;
93 if (StringEqualsLiteral(typeLinearStr, "externref")) {
94 *out = RefType::extern_();
95 return true;
97 #ifdef ENABLE_WASM_EXNREF
98 if (ExnRefAvailable(cx)) {
99 if (StringEqualsLiteral(typeLinearStr, "exnref")) {
100 *out = RefType::exn();
101 return true;
104 #endif
105 #ifdef ENABLE_WASM_GC
106 if (GcAvailable(cx)) {
107 if (StringEqualsLiteral(typeLinearStr, "anyref")) {
108 *out = RefType::any();
109 return true;
111 if (StringEqualsLiteral(typeLinearStr, "eqref")) {
112 *out = RefType::eq();
113 return true;
115 if (StringEqualsLiteral(typeLinearStr, "i31ref")) {
116 *out = RefType::i31();
117 return true;
119 if (StringEqualsLiteral(typeLinearStr, "structref")) {
120 *out = RefType::struct_();
121 return true;
123 if (StringEqualsLiteral(typeLinearStr, "arrayref")) {
124 *out = RefType::array();
125 return true;
127 if (StringEqualsLiteral(typeLinearStr, "nullfuncref")) {
128 *out = RefType::nofunc();
129 return true;
131 if (StringEqualsLiteral(typeLinearStr, "nullexternref")) {
132 *out = RefType::noextern();
133 return true;
135 if (StringEqualsLiteral(typeLinearStr, "nullref")) {
136 *out = RefType::none();
137 return true;
140 #endif
142 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
143 JSMSG_WASM_BAD_STRING_VAL_TYPE);
144 return false;
147 enum class RefTypeResult {
148 Failure,
149 Parsed,
150 Unparsed,
153 static RefTypeResult MaybeToRefType(JSContext* cx, HandleObject obj,
154 RefType* out) {
155 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
156 if (!wasm::FunctionReferencesAvailable(cx)) {
157 return RefTypeResult::Unparsed;
160 JSAtom* refAtom = Atomize(cx, "ref", strlen("ref"));
161 if (!refAtom) {
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));
172 if (!typeStr) {
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();
188 # endif
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();
200 # endif
201 } else {
202 return RefTypeResult::Unparsed;
205 JSAtom* nullableAtom = Atomize(cx, "nullable", strlen("nullable"));
206 if (!nullableAtom) {
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);
216 if (!nullable) {
217 *out = out->asNonNullable();
219 MOZ_ASSERT(out->isNullable() == nullable);
220 return RefTypeResult::Parsed;
221 #else
222 return RefTypeResult::Unparsed;
223 #endif
226 bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) {
227 if (v.isObject()) {
228 RootedObject obj(cx, &v.toObject());
229 RefType refType;
230 switch (MaybeToRefType(cx, obj, &refType)) {
231 case RefTypeResult::Failure:
232 return false;
233 case RefTypeResult::Parsed:
234 *out = ValType(refType);
235 return true;
236 case RefTypeResult::Unparsed:
237 break;
241 RootedString typeStr(cx, ToString(cx, v));
242 if (!typeStr) {
243 return false;
246 Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx));
247 if (!typeLinearStr) {
248 return false;
251 if (StringEqualsLiteral(typeLinearStr, "i32")) {
252 *out = ValType::I32;
253 } else if (StringEqualsLiteral(typeLinearStr, "i64")) {
254 *out = ValType::I64;
255 } else if (StringEqualsLiteral(typeLinearStr, "f32")) {
256 *out = ValType::F32;
257 } else if (StringEqualsLiteral(typeLinearStr, "f64")) {
258 *out = ValType::F64;
259 #ifdef ENABLE_WASM_SIMD
260 } else if (SimdAvailable(cx) && StringEqualsLiteral(typeLinearStr, "v128")) {
261 *out = ValType::V128;
262 #endif
263 } else {
264 RefType rt;
265 if (ToRefType(cx, typeLinearStr, &rt)) {
266 *out = ValType(rt);
267 } else {
268 // ToRefType will report an error when it fails, just return false
269 return false;
273 return true;
276 bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) {
277 if (v.isObject()) {
278 RootedObject obj(cx, &v.toObject());
279 switch (MaybeToRefType(cx, obj, out)) {
280 case RefTypeResult::Failure:
281 return false;
282 case RefTypeResult::Parsed:
283 return true;
284 case RefTypeResult::Unparsed:
285 break;
289 RootedString typeStr(cx, ToString(cx, v));
290 if (!typeStr) {
291 return false;
294 Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx));
295 if (!typeLinearStr) {
296 return false;
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()) {
307 case RefType::Func:
308 literal = "funcref";
309 break;
310 case RefType::Extern:
311 literal = "externref";
312 break;
313 case RefType::Exn:
314 literal = "exnref";
315 break;
316 case RefType::Any:
317 literal = "anyref";
318 break;
319 case RefType::NoFunc:
320 literal = "nullfuncref";
321 break;
322 case RefType::NoExtern:
323 literal = "nullexternref";
324 break;
325 case RefType::None:
326 literal = "nullref";
327 break;
328 case RefType::Eq:
329 literal = "eqref";
330 break;
331 case RefType::I31:
332 literal = "i31ref";
333 break;
334 case RefType::Struct:
335 literal = "structref";
336 break;
337 case RefType::Array:
338 literal = "arrayref";
339 break;
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()) {
350 case RefType::Func:
351 heapType = "func";
352 break;
353 case RefType::Extern:
354 heapType = "extern";
355 break;
356 case RefType::Exn:
357 heapType = "exn";
358 break;
359 case RefType::Any:
360 heapType = "any";
361 break;
362 case RefType::NoFunc:
363 heapType = "nofunc";
364 break;
365 case RefType::NoExtern:
366 heapType = "noextern";
367 break;
368 case RefType::None:
369 heapType = "none";
370 break;
371 case RefType::Eq:
372 heapType = "eq";
373 break;
374 case RefType::I31:
375 heapType = "i31";
376 break;
377 case RefType::Struct:
378 heapType = "struct";
379 break;
380 case RefType::Array:
381 heapType = "array";
382 break;
383 case RefType::TypeRef: {
384 if (types) {
385 uint32_t typeIndex = types->indexOf(*type.typeDef());
386 return JS_smprintf("(ref %s%d)", type.isNullable() ? "null " : "",
387 typeIndex);
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:
403 literal = "i8";
404 break;
405 case StorageType::I16:
406 literal = "i16";
407 break;
408 case StorageType::I32:
409 literal = "i32";
410 break;
411 case StorageType::I64:
412 literal = "i64";
413 break;
414 case StorageType::V128:
415 literal = "v128";
416 break;
417 case StorageType::F32:
418 literal = "f32";
419 break;
420 case StorageType::F64:
421 literal = "f64";
422 break;
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");