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 #ifndef wasm_val_h
20 #define wasm_val_h
21 
22 #include "js/Class.h"  // JSClassOps, ClassSpec
23 #include "vm/JSObject.h"
24 #include "vm/NativeObject.h"  // NativeObject
25 #include "wasm/WasmValType.h"
26 
27 namespace js {
28 namespace wasm {
29 
30 // A V128 value.
31 
32 struct V128 {
33   uint8_t bytes[16];  // Little-endian
34 
V128V12835   V128() { memset(bytes, 0, sizeof(bytes)); }
36 
37   template <typename T>
extractLaneV12838   T extractLane(unsigned lane) const {
39     T result;
40     MOZ_ASSERT(lane < 16 / sizeof(T));
41     memcpy(&result, bytes + sizeof(T) * lane, sizeof(T));
42     return result;
43   }
44 
45   template <typename T>
insertLaneV12846   void insertLane(unsigned lane, T value) {
47     MOZ_ASSERT(lane < 16 / sizeof(T));
48     memcpy(bytes + sizeof(T) * lane, &value, sizeof(T));
49   }
50 
51   bool operator==(const V128& rhs) const {
52     for (size_t i = 0; i < sizeof(bytes); i++) {
53       if (bytes[i] != rhs.bytes[i]) {
54         return false;
55       }
56     }
57     return true;
58   }
59 
60   bool operator!=(const V128& rhs) const { return !(*this == rhs); }
61 };
62 
63 static_assert(sizeof(V128) == 16, "Invariant");
64 
65 // An AnyRef is a boxed value that can represent any wasm reference type and any
66 // host type that the host system allows to flow into and out of wasm
67 // transparently.  It is a pointer-sized datum that has the same representation
68 // as all its subtypes (funcref, externref, eqref, (ref T), et al) due to the
69 // non-coercive subtyping of the wasm type system.  Its current representation
70 // is a plain JSObject*, and the private JSObject subtype WasmValueBox is used
71 // to box non-object non-null JS values.
72 //
73 // The C++/wasm boundary always uses a 'void*' type to express AnyRef values, to
74 // emphasize the pointer-ness of the value.  The C++ code must transform the
75 // void* into an AnyRef by calling AnyRef::fromCompiledCode(), and transform an
76 // AnyRef into a void* by calling AnyRef::toCompiledCode().  Once in C++, we use
77 // AnyRef everywhere.  A JS Value is transformed into an AnyRef by calling
78 // AnyRef::box(), and the AnyRef is transformed into a JS Value by calling
79 // AnyRef::unbox().
80 //
81 // NOTE that AnyRef values may point to GC'd storage and as such need to be
82 // rooted if they are kept live in boxed form across code that may cause GC!
83 // Use RootedAnyRef / HandleAnyRef / MutableHandleAnyRef where necessary.
84 //
85 // The lowest bits of the pointer value are used for tagging, to allow for some
86 // representation optimizations and to distinguish various types.
87 
88 // For version 0, we simply equate AnyRef and JSObject* (this means that there
89 // are technically no tags at all yet).  We use a simple boxing scheme that
90 // wraps a JS value that is not already JSObject in a distinguishable JSObject
91 // that holds the value, see WasmTypes.cpp for details.  Knowledge of this
92 // mapping is embedded in CodeGenerator.cpp (in WasmBoxValue and
93 // WasmAnyRefFromJSObject) and in WasmStubs.cpp (in functions Box* and Unbox*).
94 
95 class AnyRef {
96   // mutable so that tracing may access a JSObject* from a `const Val` or
97   // `const AnyRef`.
98   mutable JSObject* value_;
99 
AnyRef()100   explicit AnyRef() : value_((JSObject*)-1) {}
AnyRef(JSObject * p)101   explicit AnyRef(JSObject* p) : value_(p) {
102     MOZ_ASSERT(((uintptr_t)p & 0x03) == 0);
103   }
104 
105  public:
106   // An invalid AnyRef cannot arise naturally from wasm and so can be used as
107   // a sentinel value to indicate failure from an AnyRef-returning function.
invalid()108   static AnyRef invalid() { return AnyRef(); }
109 
110   // Given a void* that comes from compiled wasm code, turn it into AnyRef.
fromCompiledCode(void * p)111   static AnyRef fromCompiledCode(void* p) { return AnyRef((JSObject*)p); }
112 
113   // Given a JSObject* that comes from JS, turn it into AnyRef.
fromJSObject(JSObject * p)114   static AnyRef fromJSObject(JSObject* p) { return AnyRef(p); }
115 
116   // Generate an AnyRef null pointer.
null()117   static AnyRef null() { return AnyRef(nullptr); }
118 
isNull()119   bool isNull() const { return value_ == nullptr; }
120 
121   bool operator==(const AnyRef& rhs) const {
122     return this->value_ == rhs.value_;
123   }
124 
125   bool operator!=(const AnyRef& rhs) const { return !(*this == rhs); }
126 
forCompiledCode()127   void* forCompiledCode() const { return value_; }
128 
asJSObject()129   JSObject* asJSObject() const { return value_; }
130 
asJSObjectAddress()131   JSObject** asJSObjectAddress() const { return &value_; }
132 
133   void trace(JSTracer* trc);
134 
135   // Tags (to be developed further)
136   static constexpr uintptr_t AnyRefTagMask = 1;
137   static constexpr uintptr_t AnyRefObjTag = 0;
138 };
139 
140 using RootedAnyRef = Rooted<AnyRef>;
141 using HandleAnyRef = Handle<AnyRef>;
142 using MutableHandleAnyRef = MutableHandle<AnyRef>;
143 
144 // TODO/AnyRef-boxing: With boxed immediates and strings, these will be defined
145 // as MOZ_CRASH or similar so that we can find all locations that need to be
146 // fixed.
147 
148 #define ASSERT_ANYREF_IS_JSOBJECT (void)(0)
149 #define STATIC_ASSERT_ANYREF_IS_JSOBJECT static_assert(1, "AnyRef is JSObject")
150 
151 // Given any JS value, box it as an AnyRef and store it in *result.  Returns
152 // false on OOM.
153 
154 bool BoxAnyRef(JSContext* cx, HandleValue val, MutableHandleAnyRef result);
155 
156 // Given a JS value that requires an object box, box it as an AnyRef and return
157 // it, returning nullptr on OOM.
158 //
159 // Currently the values requiring a box are those other than JSObject* or
160 // nullptr, but in the future more values will be represented without an
161 // allocation.
162 JSObject* BoxBoxableValue(JSContext* cx, HandleValue val);
163 
164 // Given any AnyRef, unbox it as a JS Value.  If it is a reference to a wasm
165 // object it will be reflected as a JSObject* representing some TypedObject
166 // instance.
167 
168 Value UnboxAnyRef(AnyRef val);
169 
170 class WasmValueBox : public NativeObject {
171   static const unsigned VALUE_SLOT = 0;
172 
173  public:
174   static const unsigned RESERVED_SLOTS = 1;
175   static const JSClass class_;
176 
177   static WasmValueBox* create(JSContext* cx, HandleValue val);
value()178   Value value() const { return getFixedSlot(VALUE_SLOT); }
offsetOfValue()179   static size_t offsetOfValue() {
180     return NativeObject::getFixedSlotOffset(VALUE_SLOT);
181   }
182 };
183 
184 // A FuncRef is a JSFunction* and is hence also an AnyRef, and the remarks above
185 // about AnyRef apply also to FuncRef.  When 'funcref' is used as a value type
186 // in wasm code, the value that is held is "the canonical function value", which
187 // is a function for which IsWasmExportedFunction() is true, and which has the
188 // correct identity wrt reference equality of functions.  Notably, if a function
189 // is imported then its ref.func value compares === in JS to the function that
190 // was passed as an import when the instance was created.
191 //
192 // These rules ensure that casts from funcref to anyref are non-converting
193 // (generate no code), and that no wrapping or unwrapping needs to happen when a
194 // funcref or anyref flows across the JS/wasm boundary, and that functions have
195 // the necessary identity when observed from JS, and in the future, from wasm.
196 //
197 // Functions stored in tables, whether wasm tables or internal tables, can be
198 // stored in a form that optimizes for eg call speed, however.
199 //
200 // Reading a funcref from a funcref table, writing a funcref to a funcref table,
201 // and generating the value for a ref.func instruction are therefore nontrivial
202 // operations that require mapping between the canonical JSFunction and the
203 // optimized table representation.  Once we get an instruction to call a
204 // ref.func directly it too will require such a mapping.
205 
206 // In many cases, a FuncRef is exactly the same as AnyRef and we can use AnyRef
207 // functionality on funcref values.  The FuncRef class exists mostly to add more
208 // checks and to make it clear, when we need to, that we're manipulating funcref
209 // values.  FuncRef does not currently subclass AnyRef because there's been no
210 // need to, but it probably could.
211 
212 class FuncRef {
213   JSFunction* value_;
214 
FuncRef()215   explicit FuncRef() : value_((JSFunction*)-1) {}
FuncRef(JSFunction * p)216   explicit FuncRef(JSFunction* p) : value_(p) {
217     MOZ_ASSERT(((uintptr_t)p & 0x03) == 0);
218   }
219 
220  public:
221   // Given a void* that comes from compiled wasm code, turn it into FuncRef.
fromCompiledCode(void * p)222   static FuncRef fromCompiledCode(void* p) { return FuncRef((JSFunction*)p); }
223 
224   // Given a JSFunction* that comes from JS, turn it into FuncRef.
fromJSFunction(JSFunction * p)225   static FuncRef fromJSFunction(JSFunction* p) { return FuncRef(p); }
226 
227   // Given an AnyRef that represents a possibly-null funcref, turn it into a
228   // FuncRef.
229   static FuncRef fromAnyRefUnchecked(AnyRef p);
230 
asAnyRef()231   AnyRef asAnyRef() { return AnyRef::fromJSObject((JSObject*)value_); }
232 
forCompiledCode()233   void* forCompiledCode() const { return value_; }
234 
asJSFunction()235   JSFunction* asJSFunction() { return value_; }
236 
isNull()237   bool isNull() { return value_ == nullptr; }
238 };
239 
240 using RootedFuncRef = Rooted<FuncRef>;
241 using HandleFuncRef = Handle<FuncRef>;
242 using MutableHandleFuncRef = MutableHandle<FuncRef>;
243 
244 // Given any FuncRef, unbox it as a JS Value -- always a JSFunction*.
245 
246 Value UnboxFuncRef(FuncRef val);
247 
248 // The LitVal class represents a single WebAssembly value of a given value
249 // type, mostly for the purpose of numeric literals and initializers. A LitVal
250 // does not directly map to a JS value since there is not (currently) a precise
251 // representation of i64 values. A LitVal may contain non-canonical NaNs since,
252 // within WebAssembly, floats are not canonicalized. Canonicalization must
253 // happen at the JS boundary.
254 
255 class LitVal {
256  public:
257   union Cell {
258     int32_t i32_;
259     int64_t i64_;
260     float f32_;
261     double f64_;
262     wasm::V128 v128_;
263     wasm::AnyRef ref_;
Cell()264     Cell() : v128_() {}
265     ~Cell() = default;
266   };
267 
268  protected:
269   ValType type_;
270   Cell cell_;
271 
272  public:
LitVal()273   LitVal() : type_(ValType()), cell_{} {}
274 
LitVal(ValType type)275   explicit LitVal(ValType type) : type_(type) {
276     MOZ_ASSERT(type.isDefaultable());
277     switch (type.kind()) {
278       case ValType::Kind::I32: {
279         cell_.i32_ = 0;
280         break;
281       }
282       case ValType::Kind::I64: {
283         cell_.i64_ = 0;
284         break;
285       }
286       case ValType::Kind::F32: {
287         cell_.f32_ = 0;
288         break;
289       }
290       case ValType::Kind::F64: {
291         cell_.f64_ = 0;
292         break;
293       }
294       case ValType::Kind::V128: {
295         new (&cell_.v128_) V128();
296         break;
297       }
298       case ValType::Kind::Ref: {
299         cell_.ref_ = AnyRef::null();
300         break;
301       }
302       case ValType::Kind::Rtt: {
303         MOZ_CRASH("not defaultable");
304       }
305     }
306   }
307 
LitVal(uint32_t i32)308   explicit LitVal(uint32_t i32) : type_(ValType::I32) { cell_.i32_ = i32; }
LitVal(uint64_t i64)309   explicit LitVal(uint64_t i64) : type_(ValType::I64) { cell_.i64_ = i64; }
310 
LitVal(float f32)311   explicit LitVal(float f32) : type_(ValType::F32) { cell_.f32_ = f32; }
LitVal(double f64)312   explicit LitVal(double f64) : type_(ValType::F64) { cell_.f64_ = f64; }
313 
LitVal(V128 v128)314   explicit LitVal(V128 v128) : type_(ValType::V128) { cell_.v128_ = v128; }
315 
LitVal(ValType type,AnyRef any)316   explicit LitVal(ValType type, AnyRef any) : type_(type) {
317     MOZ_ASSERT(type.isReference());
318     MOZ_ASSERT(any.isNull(),
319                "use Val for non-nullptr ref types to get tracing");
320     cell_.ref_ = any;
321   }
322 
type()323   ValType type() const { return type_; }
sizeofLargestValue()324   static constexpr size_t sizeofLargestValue() { return sizeof(cell_); }
325 
cell()326   Cell& cell() { return cell_; }
cell()327   const Cell& cell() const { return cell_; }
328 
i32()329   uint32_t i32() const {
330     MOZ_ASSERT(type_ == ValType::I32);
331     return cell_.i32_;
332   }
i64()333   uint64_t i64() const {
334     MOZ_ASSERT(type_ == ValType::I64);
335     return cell_.i64_;
336   }
f32()337   const float& f32() const {
338     MOZ_ASSERT(type_ == ValType::F32);
339     return cell_.f32_;
340   }
f64()341   const double& f64() const {
342     MOZ_ASSERT(type_ == ValType::F64);
343     return cell_.f64_;
344   }
ref()345   AnyRef ref() const {
346     MOZ_ASSERT(type_.isReference());
347     return cell_.ref_;
348   }
v128()349   const V128& v128() const {
350     MOZ_ASSERT(type_ == ValType::V128);
351     return cell_.v128_;
352   }
353 };
354 
355 // A Val is a LitVal that can contain (non-null) pointers to GC things. All Vals
356 // must be used with the rooting APIs as they may contain JS objects.
357 
358 class MOZ_NON_PARAM Val : public LitVal {
359  public:
Val()360   Val() : LitVal() {}
Val(ValType type)361   explicit Val(ValType type) : LitVal(type) {}
362   explicit Val(const LitVal& val);
Val(uint32_t i32)363   explicit Val(uint32_t i32) : LitVal(i32) {}
Val(uint64_t i64)364   explicit Val(uint64_t i64) : LitVal(i64) {}
Val(float f32)365   explicit Val(float f32) : LitVal(f32) {}
Val(double f64)366   explicit Val(double f64) : LitVal(f64) {}
Val(V128 v128)367   explicit Val(V128 v128) : LitVal(v128) {}
Val(ValType type,AnyRef val)368   explicit Val(ValType type, AnyRef val) : LitVal(type, AnyRef::null()) {
369     MOZ_ASSERT(type.isReference());
370     cell_.ref_ = val;
371   }
Val(ValType type,FuncRef val)372   explicit Val(ValType type, FuncRef val) : LitVal(type, AnyRef::null()) {
373     MOZ_ASSERT(type.isFuncRef());
374     cell_.ref_ = val.asAnyRef();
375   }
376 
377   Val(const Val&) = default;
378   Val& operator=(const Val&) = default;
379 
380   bool operator==(const Val& rhs) const {
381     if (type_ != rhs.type_) {
382       return false;
383     }
384     switch (type_.kind()) {
385       case ValType::I32:
386         return cell_.i32_ == rhs.cell_.i32_;
387       case ValType::I64:
388         return cell_.i64_ == rhs.cell_.i64_;
389       case ValType::F32:
390         return cell_.f32_ == rhs.cell_.f32_;
391       case ValType::F64:
392         return cell_.f64_ == rhs.cell_.f64_;
393       case ValType::V128:
394         return cell_.v128_ == rhs.cell_.v128_;
395       case ValType::Rtt:
396       case ValType::Ref:
397         return cell_.ref_ == rhs.cell_.ref_;
398     }
399     MOZ_ASSERT_UNREACHABLE();
400     return false;
401   }
402   bool operator!=(const Val& rhs) const { return !(*this == rhs); }
403 
isJSObject()404   bool isJSObject() const {
405     return type_.isValid() && type_.isReference() && !cell_.ref_.isNull();
406   }
407 
asJSObject()408   JSObject* asJSObject() const {
409     MOZ_ASSERT(isJSObject());
410     return cell_.ref_.asJSObject();
411   }
412 
asJSObjectAddress()413   JSObject** asJSObjectAddress() const {
414     return cell_.ref_.asJSObjectAddress();
415   }
416 
417   void readFromRootedLocation(const void* loc);
418   void writeToRootedLocation(void* loc, bool mustWrite64) const;
419 
420   // See the comment for `ToWebAssemblyValue` below.
421   static bool fromJSValue(JSContext* cx, ValType targetType, HandleValue val,
422                           MutableHandle<Val> rval);
423   // See the comment for `ToJSValue` below.
424   bool toJSValue(JSContext* cx, MutableHandleValue rval) const;
425 
426   void trace(JSTracer* trc) const;
427 };
428 
429 using GCPtrVal = GCPtr<Val>;
430 using RootedVal = Rooted<Val>;
431 using HandleVal = Handle<Val>;
432 using MutableHandleVal = MutableHandle<Val>;
433 
434 using ValVector = GCVector<Val, 0, SystemAllocPolicy>;
435 using RootedValVector = Rooted<ValVector>;
436 using HandleValVector = Handle<ValVector>;
437 using MutableHandleValVector = MutableHandle<ValVector>;
438 
439 // Check a value against the given reference type.  If the targetType
440 // is RefType::Extern then the test always passes, but the value may be boxed.
441 // If the test passes then the value is stored either in fnval (for
442 // RefType::Func) or in refval (for other types); this split is not strictly
443 // necessary but is convenient for the users of this function.
444 //
445 // This can return false if the type check fails, or if a boxing into AnyRef
446 // throws an OOM.
447 [[nodiscard]] extern bool CheckRefType(JSContext* cx, RefType targetType,
448                                        HandleValue v,
449                                        MutableHandleFunction fnval,
450                                        MutableHandleAnyRef refval);
451 
452 // The same as above for when the target type is 'funcref'.
453 [[nodiscard]] extern bool CheckFuncRefValue(JSContext* cx, HandleValue v,
454                                             MutableHandleFunction fun);
455 
456 // The same as above for when the target type is 'eqref'.
457 [[nodiscard]] extern bool CheckEqRefValue(JSContext* cx, HandleValue v,
458                                           MutableHandleAnyRef vp);
459 class NoDebug;
460 class DebugCodegenVal;
461 
462 // The level of coercion to apply in `ToWebAssemblyValue` and `ToJSValue`.
463 enum class CoercionLevel {
464   // The default coercions given by the JS-API specification.
465   Spec,
466   // Allow for the coercions given by `Spec` but also use WebAssembly.Global
467   // as a container for lossless conversions. This is only available through
468   // the wasmLosslessInvoke testing function and is used in tests.
469   Lossless,
470 };
471 
472 // Coercion function from a JS value to a WebAssembly value [1].
473 //
474 // This function may fail for any of the following reasons:
475 //  * The input value has an incorrect type for the targetType
476 //  * The targetType is not exposable
477 //  * An OOM ocurred
478 // An error will be set upon failure.
479 //
480 // [1] https://webassembly.github.io/spec/js-api/index.html#towebassemblyvalue
481 template <typename Debug = NoDebug>
482 extern bool ToWebAssemblyValue(JSContext* cx, HandleValue val, FieldType type,
483                                void* loc, bool mustWrite64,
484                                CoercionLevel level = CoercionLevel::Spec);
485 template <typename Debug = NoDebug>
486 extern bool ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type,
487                                void* loc, bool mustWrite64,
488                                CoercionLevel level = CoercionLevel::Spec);
489 
490 // Coercion function from a WebAssembly value to a JS value [1].
491 //
492 // This function will only fail if an OOM ocurred. If the type of WebAssembly
493 // value being coerced is not exposable to JS, then it will be coerced to
494 // 'undefined'. Callers are responsible for guarding against this if this is
495 // not desirable.
496 //
497 // [1] https://webassembly.github.io/spec/js-api/index.html#tojsvalue
498 template <typename Debug = NoDebug>
499 extern bool ToJSValue(JSContext* cx, const void* src, FieldType type,
500                       MutableHandleValue dst,
501                       CoercionLevel level = CoercionLevel::Spec);
502 template <typename Debug = NoDebug>
503 extern bool ToJSValue(JSContext* cx, const void* src, ValType type,
504                       MutableHandleValue dst,
505                       CoercionLevel level = CoercionLevel::Spec);
506 
507 }  // namespace wasm
508 
509 template <>
510 struct InternalBarrierMethods<wasm::Val> {
511   STATIC_ASSERT_ANYREF_IS_JSOBJECT;
512 
513   static bool isMarkable(const wasm::Val& v) { return v.isJSObject(); }
514 
515   static void preBarrier(const wasm::Val& v) {
516     if (v.isJSObject()) {
517       gc::PreWriteBarrier(v.asJSObject());
518     }
519   }
520 
521   static MOZ_ALWAYS_INLINE void postBarrier(wasm::Val* vp,
522                                             const wasm::Val& prev,
523                                             const wasm::Val& next) {
524     MOZ_RELEASE_ASSERT(!prev.type().isValid() || prev.type() == next.type());
525     JSObject* prevObj = prev.isJSObject() ? prev.asJSObject() : nullptr;
526     JSObject* nextObj = next.isJSObject() ? next.asJSObject() : nullptr;
527     if (nextObj) {
528       JSObject::postWriteBarrier(vp->asJSObjectAddress(), prevObj, nextObj);
529     }
530   }
531 
532   static void readBarrier(const wasm::Val& v) {
533     if (v.isJSObject()) {
534       gc::ReadBarrier(v.asJSObject());
535     }
536   }
537 
538 #ifdef DEBUG
539   static void assertThingIsNotGray(const wasm::Val& v) {
540     if (v.isJSObject()) {
541       JS::AssertObjectIsNotGray(v.asJSObject());
542     }
543   }
544 #endif
545 };
546 
547 }  // namespace js
548 
549 #endif  // wasm_val_h
550