tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WasmValType.cpp (12220B)


      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:
      3 *
      4 * Copyright 2021 Mozilla Foundation
      5 *
      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
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     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.
     17 */
     18 
     19 #include "wasm/WasmValType.h"
     20 
     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"
     26 
     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"
     32 
     33 #include "vm/JSAtomUtils-inl.h"  // AtomToId
     34 #include "vm/JSObject-inl.h"
     35 
     36 using namespace js;
     37 using namespace js::wasm;
     38 
     39 RefType RefType::topType() const {
     40  switch (hierarchy()) {
     41    case wasm::RefTypeHierarchy::Any:
     42      return wasm::RefType::any();
     43    case wasm::RefTypeHierarchy::Func:
     44      return wasm::RefType::func();
     45    case wasm::RefTypeHierarchy::Extern:
     46      return wasm::RefType::extern_();
     47    case wasm::RefTypeHierarchy::Exn:
     48      return wasm::RefType::exn();
     49    default:
     50      MOZ_CRASH("switch is exhaustive");
     51  }
     52 }
     53 
     54 RefType RefType::bottomType() const {
     55  switch (hierarchy()) {
     56    case wasm::RefTypeHierarchy::Any:
     57      return wasm::RefType::none();
     58    case wasm::RefTypeHierarchy::Func:
     59      return wasm::RefType::nofunc();
     60    case wasm::RefTypeHierarchy::Extern:
     61      return wasm::RefType::noextern();
     62    case wasm::RefTypeHierarchy::Exn:
     63      return wasm::RefType::noexn();
     64    default:
     65      MOZ_CRASH("switch is exhaustive");
     66  }
     67 }
     68 
     69 static RefType FirstCommonSuperType(RefType a, RefType b,
     70                                    std::initializer_list<RefType> supers) {
     71  for (RefType super : supers) {
     72    if (RefType::isSubTypeOf(a, super) && RefType::isSubTypeOf(b, super)) {
     73      return super;
     74    }
     75  }
     76  MOZ_CRASH("failed to find common super type");
     77 }
     78 
     79 RefType RefType::leastUpperBound(RefType a, RefType b) {
     80  // Types in different hierarchies have no common bound. Validation should
     81  // always prevent two such types from being compared.
     82  MOZ_RELEASE_ASSERT(a.hierarchy() == b.hierarchy());
     83 
     84  // Whether the LUB is nullable can be determined by the nullability of a and
     85  // b, regardless of their actual types.
     86  bool nullable = a.isNullable() || b.isNullable();
     87 
     88  // If one type is a subtype of the other, the higher type is the LUB - and we
     89  // can capture nulls here too, as we know the nullability of the LUB.
     90  if (RefType::isSubTypeOf(a, b.withIsNullable(nullable))) {
     91    return b.withIsNullable(nullable);
     92  }
     93  if (RefType::isSubTypeOf(b, a.withIsNullable(nullable))) {
     94    return a.withIsNullable(nullable);
     95  }
     96 
     97  // Concrete types may share a concrete parent type. We can test b against all
     98  // of a's parent types to see if this is true.
     99  if (a.isTypeRef() && b.isTypeRef()) {
    100    const TypeDef* aSuper = a.typeDef()->superTypeDef();
    101    while (aSuper) {
    102      if (TypeDef::isSubTypeOf(b.typeDef(), aSuper)) {
    103        return RefType(aSuper, nullable);
    104      }
    105      aSuper = aSuper->superTypeDef();
    106    }
    107  }
    108 
    109  // Because wasm type hierarchies are pretty small and simple, we can
    110  // essentially brute-force the LUB by simply iterating over all the abstract
    111  // types bottom-to-top. The first one that is a super type of both a and b is
    112  // the LUB. We are guaranteed to find a common bound because we have verified
    113  // that the types have the same hierarchy and we will therefore at least find
    114  // the hierarchy's top type.
    115  //
    116  // We test against the nullable versions of these types, and then apply the
    117  // true nullability afterward. This is ok -- this finds the *kind* of the LUB
    118  // (which we now know to be abstract), and applying the correct nullability
    119  // will not affect this. For example, for the non-nullable types
    120  // (ref $myStruct) and (ref $myArray), we will find (ref null eq), and then
    121  // modify it to (ref eq), which is the correct LUB.
    122  RefType common;
    123  switch (a.hierarchy()) {
    124    case RefTypeHierarchy::Any:
    125      common = FirstCommonSuperType(
    126          a, b,
    127          {RefType::none(), RefType::i31(), RefType::struct_(),
    128           RefType::array(), RefType::eq(), RefType::any()});
    129      break;
    130    case RefTypeHierarchy::Func:
    131      common = FirstCommonSuperType(a, b, {RefType::nofunc(), RefType::func()});
    132      break;
    133    case RefTypeHierarchy::Extern:
    134      common =
    135          FirstCommonSuperType(a, b, {RefType::noextern(), RefType::extern_()});
    136      break;
    137    case RefTypeHierarchy::Exn:
    138      common = FirstCommonSuperType(a, b, {RefType::noexn(), RefType::exn()});
    139      break;
    140    default:
    141      MOZ_CRASH("unknown type hierarchy");
    142  }
    143  return common.withIsNullable(nullable);
    144 }
    145 
    146 TypeDefKind RefType::typeDefKind() const {
    147  switch (kind()) {
    148    case RefType::Struct:
    149      return TypeDefKind::Struct;
    150    case RefType::Array:
    151      return TypeDefKind::Array;
    152    case RefType::Func:
    153      return TypeDefKind::Func;
    154    default:
    155      return TypeDefKind::None;
    156  }
    157  MOZ_CRASH("switch is exhaustive");
    158 }
    159 
    160 static bool ToRefType(JSContext* cx, const JSLinearString* typeLinearStr,
    161                      RefType* out) {
    162  if (StringEqualsLiteral(typeLinearStr, "anyfunc") ||
    163      StringEqualsLiteral(typeLinearStr, "funcref")) {
    164    // The JS API uses "anyfunc" uniformly as the external name of funcref.  We
    165    // also allow "funcref" for compatibility with code we've already shipped.
    166    *out = RefType::func();
    167    return true;
    168  }
    169  if (StringEqualsLiteral(typeLinearStr, "externref")) {
    170    *out = RefType::extern_();
    171    return true;
    172  }
    173  if (StringEqualsLiteral(typeLinearStr, "exnref")) {
    174    *out = RefType::exn();
    175    return true;
    176  }
    177  if (StringEqualsLiteral(typeLinearStr, "anyref")) {
    178    *out = RefType::any();
    179    return true;
    180  }
    181  if (StringEqualsLiteral(typeLinearStr, "eqref")) {
    182    *out = RefType::eq();
    183    return true;
    184  }
    185  if (StringEqualsLiteral(typeLinearStr, "i31ref")) {
    186    *out = RefType::i31();
    187    return true;
    188  }
    189  if (StringEqualsLiteral(typeLinearStr, "structref")) {
    190    *out = RefType::struct_();
    191    return true;
    192  }
    193  if (StringEqualsLiteral(typeLinearStr, "arrayref")) {
    194    *out = RefType::array();
    195    return true;
    196  }
    197  if (StringEqualsLiteral(typeLinearStr, "nullfuncref")) {
    198    *out = RefType::nofunc();
    199    return true;
    200  }
    201  if (StringEqualsLiteral(typeLinearStr, "nullexternref")) {
    202    *out = RefType::noextern();
    203    return true;
    204  }
    205  if (StringEqualsLiteral(typeLinearStr, "nullexnref")) {
    206    *out = RefType::noexn();
    207    return true;
    208  }
    209  if (StringEqualsLiteral(typeLinearStr, "nullref")) {
    210    *out = RefType::none();
    211    return true;
    212  }
    213 
    214  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    215                           JSMSG_WASM_BAD_STRING_VAL_TYPE);
    216  return false;
    217 }
    218 
    219 enum class RefTypeResult {
    220  Failure,
    221  Parsed,
    222  Unparsed,
    223 };
    224 
    225 bool wasm::ToValType(JSContext* cx, HandleValue v, ValType* out) {
    226  RootedString typeStr(cx, ToString(cx, v));
    227  if (!typeStr) {
    228    return false;
    229  }
    230 
    231  Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx));
    232  if (!typeLinearStr) {
    233    return false;
    234  }
    235 
    236  if (StringEqualsLiteral(typeLinearStr, "i32")) {
    237    *out = ValType::I32;
    238  } else if (StringEqualsLiteral(typeLinearStr, "i64")) {
    239    *out = ValType::I64;
    240  } else if (StringEqualsLiteral(typeLinearStr, "f32")) {
    241    *out = ValType::F32;
    242  } else if (StringEqualsLiteral(typeLinearStr, "f64")) {
    243    *out = ValType::F64;
    244 #ifdef ENABLE_WASM_SIMD
    245  } else if (SimdAvailable(cx) && StringEqualsLiteral(typeLinearStr, "v128")) {
    246    *out = ValType::V128;
    247 #endif
    248  } else {
    249    RefType rt;
    250    if (ToRefType(cx, typeLinearStr, &rt)) {
    251      *out = ValType(rt);
    252    } else {
    253      // ToRefType will report an error when it fails, just return false
    254      return false;
    255    }
    256  }
    257 
    258  return true;
    259 }
    260 
    261 bool wasm::ToRefType(JSContext* cx, HandleValue v, RefType* out) {
    262  RootedString typeStr(cx, ToString(cx, v));
    263  if (!typeStr) {
    264    return false;
    265  }
    266 
    267  Rooted<JSLinearString*> typeLinearStr(cx, typeStr->ensureLinear(cx));
    268  if (!typeLinearStr) {
    269    return false;
    270  }
    271 
    272  return ToRefType(cx, typeLinearStr, out);
    273 }
    274 
    275 UniqueChars wasm::ToString(RefType type, const TypeContext* types) {
    276  // Try to emit a shorthand version first
    277  if (type.isNullable() && !type.isTypeRef()) {
    278    const char* literal = nullptr;
    279    switch (type.kind()) {
    280      case RefType::Func:
    281        literal = "funcref";
    282        break;
    283      case RefType::Extern:
    284        literal = "externref";
    285        break;
    286      case RefType::Exn:
    287        literal = "exnref";
    288        break;
    289      case RefType::Any:
    290        literal = "anyref";
    291        break;
    292      case RefType::NoFunc:
    293        literal = "nullfuncref";
    294        break;
    295      case RefType::NoExn:
    296        literal = "nullexnref";
    297        break;
    298      case RefType::NoExtern:
    299        literal = "nullexternref";
    300        break;
    301      case RefType::None:
    302        literal = "nullref";
    303        break;
    304      case RefType::Eq:
    305        literal = "eqref";
    306        break;
    307      case RefType::I31:
    308        literal = "i31ref";
    309        break;
    310      case RefType::Struct:
    311        literal = "structref";
    312        break;
    313      case RefType::Array:
    314        literal = "arrayref";
    315        break;
    316      case RefType::TypeRef: {
    317        MOZ_CRASH("type ref should not be possible here");
    318      }
    319    }
    320    return DuplicateString(literal);
    321  }
    322 
    323  // Emit the full reference type with heap type
    324  const char* heapType = nullptr;
    325  switch (type.kind()) {
    326    case RefType::Func:
    327      heapType = "func";
    328      break;
    329    case RefType::Extern:
    330      heapType = "extern";
    331      break;
    332    case RefType::Exn:
    333      heapType = "exn";
    334      break;
    335    case RefType::Any:
    336      heapType = "any";
    337      break;
    338    case RefType::NoFunc:
    339      heapType = "nofunc";
    340      break;
    341    case RefType::NoExn:
    342      heapType = "noexn";
    343      break;
    344    case RefType::NoExtern:
    345      heapType = "noextern";
    346      break;
    347    case RefType::None:
    348      heapType = "none";
    349      break;
    350    case RefType::Eq:
    351      heapType = "eq";
    352      break;
    353    case RefType::I31:
    354      heapType = "i31";
    355      break;
    356    case RefType::Struct:
    357      heapType = "struct";
    358      break;
    359    case RefType::Array:
    360      heapType = "array";
    361      break;
    362    case RefType::TypeRef: {
    363      if (types) {
    364        uint32_t typeIndex = types->indexOf(*type.typeDef());
    365        return JS_smprintf("(ref %s%d)", type.isNullable() ? "null " : "",
    366                           typeIndex);
    367      }
    368      return JS_smprintf("(ref %s?)", type.isNullable() ? "null " : "");
    369    }
    370  }
    371  return JS_smprintf("(ref %s%s)", type.isNullable() ? "null " : "", heapType);
    372 }
    373 
    374 UniqueChars wasm::ToString(ValType type, const TypeContext* types) {
    375  return ToString(type.storageType(), types);
    376 }
    377 
    378 UniqueChars wasm::ToString(StorageType type, const TypeContext* types) {
    379  const char* literal = nullptr;
    380  switch (type.kind()) {
    381    case StorageType::I8:
    382      literal = "i8";
    383      break;
    384    case StorageType::I16:
    385      literal = "i16";
    386      break;
    387    case StorageType::I32:
    388      literal = "i32";
    389      break;
    390    case StorageType::I64:
    391      literal = "i64";
    392      break;
    393    case StorageType::V128:
    394      literal = "v128";
    395      break;
    396    case StorageType::F32:
    397      literal = "f32";
    398      break;
    399    case StorageType::F64:
    400      literal = "f64";
    401      break;
    402    case StorageType::Ref:
    403      return ToString(type.refType(), types);
    404  }
    405  return DuplicateString(literal);
    406 }
    407 
    408 UniqueChars wasm::ToString(const mozilla::Maybe<ValType>& type,
    409                           const TypeContext* types) {
    410  return type ? ToString(type.ref(), types) : JS_smprintf("%s", "void");
    411 }
    412 
    413 UniqueChars wasm::ToString(const MaybeRefType& type, const TypeContext* types) {
    414  return type ? ToString(type.value(), types) : JS_smprintf("%s", "void");
    415 }