diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/wasm/WasmValType.cpp | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/js/src/wasm/WasmValType.cpp b/js/src/wasm/WasmValType.cpp new file mode 100644 index 0000000000..d1874b7131 --- /dev/null +++ b/js/src/wasm/WasmValType.cpp @@ -0,0 +1,432 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * + * Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wasm/WasmValType.h" + +#include "js/Conversions.h" +#include "js/ErrorReport.h" +#include "js/friend/ErrorMessages.h" // JSMSG_* +#include "js/Printf.h" +#include "js/Value.h" + +#include "vm/JSAtomUtils.h" // Atomize +#include "vm/JSObject.h" +#include "vm/StringType.h" +#include "wasm/WasmFeatures.h" +#include "wasm/WasmJS.h" + +#include "vm/JSAtomUtils-inl.h" // AtomToId +#include "vm/JSObject-inl.h" + +using namespace js; +using namespace js::wasm; + +RefType RefType::topType() const { + switch (kind()) { + case RefType::Any: + case RefType::Eq: + case RefType::I31: + case RefType::Array: + case RefType::Struct: + case RefType::None: + return RefType::any(); + case RefType::Func: + case RefType::NoFunc: + return RefType::func(); + case RefType::Extern: + case RefType::NoExtern: + return RefType::extern_(); + case RefType::Exn: + return RefType::exn(); + case RefType::TypeRef: + switch (typeDef()->kind()) { + case TypeDefKind::Array: + case TypeDefKind::Struct: + return RefType::any(); + case TypeDefKind::Func: + return RefType::func(); + case TypeDefKind::None: + MOZ_CRASH("should not see TypeDefKind::None at this point"); + } + } + MOZ_CRASH("switch is exhaustive"); +} + +TypeDefKind RefType::typeDefKind() const { + switch (kind()) { + case RefType::Struct: + return TypeDefKind::Struct; + case RefType::Array: + return TypeDefKind::Array; + case RefType::Func: + return TypeDefKind::Func; + default: + return TypeDefKind::None; + } + MOZ_CRASH("switch is exhaustive"); +} + +static bool ToRefType(JSContext* cx, JSLinearString* typeLinearStr, + RefType* out) { + if (StringEqualsLiteral(typeLinearStr, "anyfunc") || + StringEqualsLiteral(typeLinearStr, "funcref")) { + // The JS API uses "anyfunc" uniformly as the external name of funcref. We + // also allow "funcref" for compatibility with code we've already shipped. + *out = RefType::func(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "externref")) { + *out = RefType::extern_(); + return true; + } +#ifdef ENABLE_WASM_EXNREF + if (ExnRefAvailable(cx)) { + if (StringEqualsLiteral(typeLinearStr, "exnref")) { + *out = RefType::exn(); + return true; + } + } +#endif +#ifdef ENABLE_WASM_GC + if (GcAvailable(cx)) { + if (StringEqualsLiteral(typeLinearStr, "anyref")) { + *out = RefType::any(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "eqref")) { + *out = RefType::eq(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "i31ref")) { + *out = RefType::i31(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "structref")) { + *out = RefType::struct_(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "arrayref")) { + *out = RefType::array(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "nullfuncref")) { + *out = RefType::nofunc(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "nullexternref")) { + *out = RefType::noextern(); + return true; + } + if (StringEqualsLiteral(typeLinearStr, "nullref")) { + *out = RefType::none(); + return true; + } + } +#endif + + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, + JSMSG_WASM_BAD_STRING_VAL_TYPE); + return false; +} + +enum class RefTypeResult { + Failure, + Parsed, + Unparsed, +}; + +static RefTypeResult MaybeToRefType(JSContext* cx, HandleObject obj, + RefType* out) { +#ifdef ENABLE_WASM_FUNCTION_REFERENCES + if (!wasm::FunctionReferencesAvailable(cx)) { + return RefTypeResult::Unparsed; + } + + JSAtom* refAtom = Atomize(cx, "ref", strlen("ref")); + if (!refAtom) { + return RefTypeResult::Failure; + } + RootedId refId(cx, AtomToId(refAtom)); + + RootedValue refVal(cx); + if (!GetProperty(cx, obj, obj, refId, &refVal)) { + return RefTypeResult::Failure; + } + + RootedString typeStr(cx, ToString(cx, refVal)); + if (!typeStr) { + return RefTypeResult::Failure; + } + + Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx)); + if (!typeLinearStr) { + return RefTypeResult::Failure; + } + + if (StringEqualsLiteral(typeLinearStr, "func")) { + *out = RefType::func(); + } else if (StringEqualsLiteral(typeLinearStr, "extern")) { + *out = RefType::extern_(); +# ifdef ENABLE_WASM_EXNREF + } else if (ExnRefAvailable(cx) && StringEqualsLiteral(typeLinearStr, "exn")) { + *out = RefType::exn(); +# endif +# ifdef ENABLE_WASM_GC + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "any")) { + *out = RefType::any(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "eq")) { + *out = RefType::eq(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "i31")) { + *out = RefType::i31(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "struct")) { + *out = RefType::struct_(); + } else if (GcAvailable(cx) && StringEqualsLiteral(typeLinearStr, "array")) { + *out = RefType::array(); +# endif + } else { + return RefTypeResult::Unparsed; + } + + JSAtom* nullableAtom = Atomize(cx, "nullable", strlen("nullable")); + if (!nullableAtom) { + return RefTypeResult::Failure; + } + RootedId nullableId(cx, AtomToId(nullableAtom)); + RootedValue nullableVal(cx); + if (!GetProperty(cx, obj, obj, nullableId, &nullableVal)) { + return RefTypeResult::Failure; + } + + bool nullable = ToBoolean(nullableVal); + if (!nullable) { + *out = out->asNonNullable(); + } + MOZ_ASSERT(out->isNullable() == nullable); + return RefTypeResult::Parsed; +#else + return RefTypeResult::Unparsed; +#endif +} + +bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) { + if (v.isObject()) { + RootedObject obj(cx, &v.toObject()); + RefType refType; + switch (MaybeToRefType(cx, obj, &refType)) { + case RefTypeResult::Failure: + return false; + case RefTypeResult::Parsed: + *out = ValType(refType); + return true; + case RefTypeResult::Unparsed: + break; + } + } + + RootedString typeStr(cx, ToString(cx, v)); + if (!typeStr) { + return false; + } + + Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx)); + if (!typeLinearStr) { + return false; + } + + if (StringEqualsLiteral(typeLinearStr, "i32")) { + *out = ValType::I32; + } else if (StringEqualsLiteral(typeLinearStr, "i64")) { + *out = ValType::I64; + } else if (StringEqualsLiteral(typeLinearStr, "f32")) { + *out = ValType::F32; + } else if (StringEqualsLiteral(typeLinearStr, "f64")) { + *out = ValType::F64; +#ifdef ENABLE_WASM_SIMD + } else if (SimdAvailable(cx) && StringEqualsLiteral(typeLinearStr, "v128")) { + *out = ValType::V128; +#endif + } else { + RefType rt; + if (ToRefType(cx, typeLinearStr, &rt)) { + *out = ValType(rt); + } else { + // ToRefType will report an error when it fails, just return false + return false; + } + } + + return true; +} + +bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) { + if (v.isObject()) { + RootedObject obj(cx, &v.toObject()); + switch (MaybeToRefType(cx, obj, out)) { + case RefTypeResult::Failure: + return false; + case RefTypeResult::Parsed: + return true; + case RefTypeResult::Unparsed: + break; + } + } + + RootedString typeStr(cx, ToString(cx, v)); + if (!typeStr) { + return false; + } + + Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx)); + if (!typeLinearStr) { + return false; + } + + return ToRefType(cx, typeLinearStr, out); +} + +UniqueChars wasm::ToString(RefType type, const TypeContext* types) { + // Try to emit a shorthand version first + if (type.isNullable() && !type.isTypeRef()) { + const char* literal = nullptr; + switch (type.kind()) { + case RefType::Func: + literal = "funcref"; + break; + case RefType::Extern: + literal = "externref"; + break; + case RefType::Exn: + literal = "exnref"; + break; + case RefType::Any: + literal = "anyref"; + break; + case RefType::NoFunc: + literal = "nullfuncref"; + break; + case RefType::NoExtern: + literal = "nullexternref"; + break; + case RefType::None: + literal = "nullref"; + break; + case RefType::Eq: + literal = "eqref"; + break; + case RefType::I31: + literal = "i31ref"; + break; + case RefType::Struct: + literal = "structref"; + break; + case RefType::Array: + literal = "arrayref"; + break; + case RefType::TypeRef: { + MOZ_CRASH("type ref should not be possible here"); + } + } + return DuplicateString(literal); + } + + // Emit the full reference type with heap type + const char* heapType = nullptr; + switch (type.kind()) { + case RefType::Func: + heapType = "func"; + break; + case RefType::Extern: + heapType = "extern"; + break; + case RefType::Exn: + heapType = "exn"; + break; + case RefType::Any: + heapType = "any"; + break; + case RefType::NoFunc: + heapType = "nofunc"; + break; + case RefType::NoExtern: + heapType = "noextern"; + break; + case RefType::None: + heapType = "none"; + break; + case RefType::Eq: + heapType = "eq"; + break; + case RefType::I31: + heapType = "i31"; + break; + case RefType::Struct: + heapType = "struct"; + break; + case RefType::Array: + heapType = "array"; + break; + case RefType::TypeRef: { + if (types) { + uint32_t typeIndex = types->indexOf(*type.typeDef()); + return JS_smprintf("(ref %s%d)", type.isNullable() ? "null " : "", + typeIndex); + } + return JS_smprintf("(ref %s?)", type.isNullable() ? "null " : ""); + } + } + return JS_smprintf("(ref %s%s)", type.isNullable() ? "null " : "", heapType); +} + +UniqueChars wasm::ToString(ValType type, const TypeContext* types) { + return ToString(type.storageType(), types); +} + +UniqueChars wasm::ToString(StorageType type, const TypeContext* types) { + const char* literal = nullptr; + switch (type.kind()) { + case StorageType::I8: + literal = "i8"; + break; + case StorageType::I16: + literal = "i16"; + break; + case StorageType::I32: + literal = "i32"; + break; + case StorageType::I64: + literal = "i64"; + break; + case StorageType::V128: + literal = "v128"; + break; + case StorageType::F32: + literal = "f32"; + break; + case StorageType::F64: + literal = "f64"; + break; + case StorageType::Ref: + return ToString(type.refType(), types); + } + return DuplicateString(literal); +} + +UniqueChars wasm::ToString(const Maybe<ValType>& type, + const TypeContext* types) { + return type ? ToString(type.ref(), types) : JS_smprintf("%s", "void"); +} |