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.
22 #include "js/Class.h" // JSClassOps, ClassSpec
23 #include "vm/JSObject.h"
24 #include "vm/NativeObject.h" // NativeObject
25 #include "wasm/WasmAnyRef.h"
26 #include "wasm/WasmSerialize.h"
27 #include "wasm/WasmTypeDef.h"
35 uint8_t bytes
[16] = {}; // Little-endian
37 WASM_CHECK_CACHEABLE_POD(bytes
);
41 explicit V128(uint8_t splatValue
) {
42 memset(bytes
, int(splatValue
), sizeof(bytes
));
46 void extractLane(unsigned lane
, T
* result
) const {
47 MOZ_ASSERT(lane
< 16 / sizeof(T
));
48 memcpy(result
, bytes
+ sizeof(T
) * lane
, sizeof(T
));
52 void insertLane(unsigned lane
, T value
) {
53 MOZ_ASSERT(lane
< 16 / sizeof(T
));
54 memcpy(bytes
+ sizeof(T
) * lane
, &value
, sizeof(T
));
57 bool operator==(const V128
& rhs
) const {
58 for (size_t i
= 0; i
< sizeof(bytes
); i
++) {
59 if (bytes
[i
] != rhs
.bytes
[i
]) {
66 bool operator!=(const V128
& rhs
) const { return !(*this == rhs
); }
69 WASM_DECLARE_CACHEABLE_POD(V128
);
71 static_assert(sizeof(V128
) == 16, "Invariant");
73 // A FuncRef is a JSFunction* and is hence also an AnyRef, and the remarks above
74 // about AnyRef apply also to FuncRef. When 'funcref' is used as a value type
75 // in wasm code, the value that is held is "the canonical function value", which
76 // is a function for which IsWasmExportedFunction() is true, and which has the
77 // correct identity wrt reference equality of functions. Notably, if a function
78 // is imported then its ref.func value compares === in JS to the function that
79 // was passed as an import when the instance was created.
81 // These rules ensure that casts from funcref to anyref are non-converting
82 // (generate no code), and that no wrapping or unwrapping needs to happen when a
83 // funcref or anyref flows across the JS/wasm boundary, and that functions have
84 // the necessary identity when observed from JS, and in the future, from wasm.
86 // Functions stored in tables, whether wasm tables or internal tables, can be
87 // stored in a form that optimizes for eg call speed, however.
89 // Reading a funcref from a funcref table, writing a funcref to a funcref table,
90 // and generating the value for a ref.func instruction are therefore nontrivial
91 // operations that require mapping between the canonical JSFunction and the
92 // optimized table representation. Once we get an instruction to call a
93 // ref.func directly it too will require such a mapping.
95 // In many cases, a FuncRef is exactly the same as AnyRef and we can use AnyRef
96 // functionality on funcref values. The FuncRef class exists mostly to add more
97 // checks and to make it clear, when we need to, that we're manipulating funcref
98 // values. FuncRef does not currently subclass AnyRef because there's been no
99 // need to, but it probably could.
102 // mutable so that tracing may access a JSFunction* from a `const FuncRef`
103 mutable JSFunction
* value_
;
105 explicit FuncRef() : value_((JSFunction
*)-1) {}
106 explicit FuncRef(JSFunction
* p
) : value_(p
) {
107 MOZ_ASSERT(((uintptr_t)p
& 0x03) == 0);
111 // Given a void* that comes from compiled wasm code, turn it into FuncRef.
112 static FuncRef
fromCompiledCode(void* p
) { return FuncRef((JSFunction
*)p
); }
114 // Given a JSFunction* that comes from JS, turn it into FuncRef.
115 static FuncRef
fromJSFunction(JSFunction
* p
) { return FuncRef(p
); }
117 // Given an AnyRef that represents a possibly-null funcref, turn it into a
119 static FuncRef
fromAnyRefUnchecked(AnyRef p
);
121 static FuncRef
null() { return FuncRef(nullptr); }
123 AnyRef
toAnyRef() { return AnyRef::fromJSObjectOrNull((JSObject
*)value_
); }
125 void* forCompiledCode() const { return value_
; }
127 JSFunction
* asJSFunction() { return value_
; }
129 bool isNull() const { return value_
== nullptr; }
131 void trace(JSTracer
* trc
) const;
134 using RootedFuncRef
= Rooted
<FuncRef
>;
135 using HandleFuncRef
= Handle
<FuncRef
>;
136 using MutableHandleFuncRef
= MutableHandle
<FuncRef
>;
138 // Given any FuncRef, unbox it as a JS Value -- always a JSFunction*.
140 Value
UnboxFuncRef(FuncRef val
);
142 // The LitVal class represents a single WebAssembly value of a given value
143 // type, mostly for the purpose of numeric literals and initializers. A LitVal
144 // does not directly map to a JS value since there is not (currently) a precise
145 // representation of i64 values. A LitVal may contain non-canonical NaNs since,
146 // within WebAssembly, floats are not canonicalized. Canonicalization must
147 // happen at the JS boundary.
157 // Mutable so that it can be traced
158 mutable wasm::AnyRef ref_
;
163 WASM_CHECK_CACHEABLE_POD(i32_
, i64_
, f32_
, f64_
, v128_
);
164 WASM_ALLOW_NON_CACHEABLE_POD_FIELD(
166 "The pointer value in ref_ is guaranteed to always be null in a "
177 explicit LitVal(ValType type
) : type_(type
) {
178 switch (type
.kind()) {
179 case ValType::Kind::I32
: {
183 case ValType::Kind::I64
: {
187 case ValType::Kind::F32
: {
191 case ValType::Kind::F64
: {
195 case ValType::Kind::V128
: {
196 new (&cell_
.v128_
) V128();
199 case ValType::Kind::Ref
: {
200 cell_
.ref_
= nullptr;
206 explicit LitVal(uint32_t i32
) : type_(ValType::I32
) { cell_
.i32_
= i32
; }
207 explicit LitVal(uint64_t i64
) : type_(ValType::I64
) { cell_
.i64_
= i64
; }
209 explicit LitVal(float f32
) : type_(ValType::F32
) { cell_
.f32_
= f32
; }
210 explicit LitVal(double f64
) : type_(ValType::F64
) { cell_
.f64_
= f64
; }
212 explicit LitVal(V128 v128
) : type_(ValType::V128
) { cell_
.v128_
= v128
; }
214 explicit LitVal(ValType type
, AnyRef any
) : type_(type
) {
215 MOZ_ASSERT(type
.isRefRepr());
216 MOZ_ASSERT(any
.isNull(),
217 "use Val for non-nullptr ref types to get tracing");
221 ValType
type() const { return type_
; }
222 static constexpr size_t sizeofLargestValue() { return sizeof(cell_
); }
224 Cell
& cell() { return cell_
; }
225 const Cell
& cell() const { return cell_
; }
227 // Updates the type of the LitVal. Does not check that the type is valid for
228 // the actual value, so make sure the type is definitely correct via
229 // validation or something.
230 void unsafeSetType(ValType type
) { type_
= type
; }
232 uint32_t i32() const {
233 MOZ_ASSERT(type_
== ValType::I32
);
236 uint64_t i64() const {
237 MOZ_ASSERT(type_
== ValType::I64
);
240 const float& f32() const {
241 MOZ_ASSERT(type_
== ValType::F32
);
244 const double& f64() const {
245 MOZ_ASSERT(type_
== ValType::F64
);
249 MOZ_ASSERT(type_
.isRefRepr());
252 const V128
& v128() const {
253 MOZ_ASSERT(type_
== ValType::V128
);
257 WASM_DECLARE_FRIEND_SERIALIZE(LitVal
);
260 WASM_DECLARE_CACHEABLE_POD(LitVal::Cell
);
262 // A Val is a LitVal that can contain (non-null) pointers to GC things. All Vals
263 // must be used with the rooting APIs as they may contain JS objects.
265 class MOZ_NON_PARAM Val
: public LitVal
{
268 explicit Val(ValType type
) : LitVal(type
) {}
269 explicit Val(const LitVal
& val
);
270 explicit Val(uint32_t i32
) : LitVal(i32
) {}
271 explicit Val(uint64_t i64
) : LitVal(i64
) {}
272 explicit Val(float f32
) : LitVal(f32
) {}
273 explicit Val(double f64
) : LitVal(f64
) {}
274 explicit Val(V128 v128
) : LitVal(v128
) {}
275 explicit Val(ValType type
, AnyRef val
) : LitVal(type
, AnyRef::null()) {
276 MOZ_ASSERT(type
.isRefRepr());
279 explicit Val(ValType type
, FuncRef val
) : LitVal(type
, AnyRef::null()) {
280 MOZ_ASSERT(type
.refType().isFuncHierarchy());
281 cell_
.ref_
= val
.toAnyRef();
284 Val(const Val
&) = default;
285 Val
& operator=(const Val
&) = default;
287 bool operator==(const Val
& rhs
) const {
288 if (type_
!= rhs
.type_
) {
291 switch (type_
.kind()) {
293 return cell_
.i32_
== rhs
.cell_
.i32_
;
295 return cell_
.i64_
== rhs
.cell_
.i64_
;
297 return cell_
.f32_
== rhs
.cell_
.f32_
;
299 return cell_
.f64_
== rhs
.cell_
.f64_
;
301 return cell_
.v128_
== rhs
.cell_
.v128_
;
303 return cell_
.ref_
== rhs
.cell_
.ref_
;
305 MOZ_ASSERT_UNREACHABLE();
308 bool operator!=(const Val
& rhs
) const { return !(*this == rhs
); }
310 bool isInvalid() const { return !type_
.isValid(); }
311 bool isAnyRef() const { return type_
.isValid() && type_
.isRefRepr(); }
312 AnyRef
& toAnyRef() const {
313 MOZ_ASSERT(isAnyRef());
317 // Initialize from `loc` which is a rooted location and needs no barriers.
318 void initFromRootedLocation(ValType type
, const void* loc
);
319 void initFromHeapLocation(ValType type
, const void* loc
);
321 // Write to `loc` which is a rooted location and needs no barriers.
322 void writeToRootedLocation(void* loc
, bool mustWrite64
) const;
324 // Read from `loc` which is in the heap.
325 void readFromHeapLocation(const void* loc
);
326 // Write to `loc` which is in the heap and must be barriered.
327 void writeToHeapLocation(void* loc
) const;
329 // See the comment for `ToWebAssemblyValue` below.
330 static bool fromJSValue(JSContext
* cx
, ValType targetType
, HandleValue val
,
331 MutableHandle
<Val
> rval
);
332 // See the comment for `ToJSValue` below.
333 bool toJSValue(JSContext
* cx
, MutableHandleValue rval
) const;
335 void trace(JSTracer
* trc
) const;
338 using GCPtrVal
= GCPtr
<Val
>;
339 using RootedVal
= Rooted
<Val
>;
340 using HandleVal
= Handle
<Val
>;
341 using MutableHandleVal
= MutableHandle
<Val
>;
343 using ValVector
= GCVector
<Val
, 0, SystemAllocPolicy
>;
344 using RootedValVector
= Rooted
<ValVector
>;
345 using HandleValVector
= Handle
<ValVector
>;
346 using MutableHandleValVector
= MutableHandle
<ValVector
>;
349 using ValVectorN
= GCVector
<Val
, N
, SystemAllocPolicy
>;
351 using RootedValVectorN
= Rooted
<ValVectorN
<N
>>;
353 // Check a value against the given reference type. If the targetType
354 // is RefType::Extern then the test always passes, but the value may be boxed.
355 // If the test passes then the value is stored either in fnval (for
356 // RefType::Func) or in refval (for other types); this split is not strictly
357 // necessary but is convenient for the users of this function.
359 // This can return false if the type check fails, or if a boxing into AnyRef
361 [[nodiscard
]] extern bool CheckRefType(JSContext
* cx
, RefType targetType
,
363 MutableHandleFunction fnval
,
364 MutableHandleAnyRef refval
);
366 // The same as above for when the target type is 'funcref'.
367 [[nodiscard
]] extern bool CheckFuncRefValue(JSContext
* cx
, HandleValue v
,
368 MutableHandleFunction fun
);
370 // The same as above for when the target type is 'anyref'.
371 [[nodiscard
]] extern bool CheckAnyRefValue(JSContext
* cx
, HandleValue v
,
372 MutableHandleAnyRef vp
);
374 // The same as above for when the target type is 'nullexternref'.
375 [[nodiscard
]] extern bool CheckNullExternRefValue(JSContext
* cx
, HandleValue v
,
376 MutableHandleAnyRef vp
);
378 // The same as above for when the target type is 'nullfuncref'.
379 [[nodiscard
]] extern bool CheckNullFuncRefValue(JSContext
* cx
, HandleValue v
,
380 MutableHandleFunction fun
);
382 // The same as above for when the target type is 'nullref'.
383 [[nodiscard
]] extern bool CheckNullRefValue(JSContext
* cx
, HandleValue v
,
384 MutableHandleAnyRef vp
);
386 // The same as above for when the target type is 'eqref'.
387 [[nodiscard
]] extern bool CheckEqRefValue(JSContext
* cx
, HandleValue v
,
388 MutableHandleAnyRef vp
);
390 // The same as above for when the target type is 'i31ref'.
391 [[nodiscard
]] extern bool CheckI31RefValue(JSContext
* cx
, HandleValue v
,
392 MutableHandleAnyRef vp
);
394 // The same as above for when the target type is 'structref'.
395 [[nodiscard
]] extern bool CheckStructRefValue(JSContext
* cx
, HandleValue v
,
396 MutableHandleAnyRef vp
);
398 // The same as above for when the target type is 'arrayref'.
399 [[nodiscard
]] extern bool CheckArrayRefValue(JSContext
* cx
, HandleValue v
,
400 MutableHandleAnyRef vp
);
402 // The same as above for when the target type is '(ref T)'.
403 [[nodiscard
]] extern bool CheckTypeRefValue(JSContext
* cx
,
404 const TypeDef
* typeDef
,
406 MutableHandleAnyRef vp
);
408 class DebugCodegenVal
;
410 // The level of coercion to apply in `ToWebAssemblyValue` and `ToJSValue`.
411 enum class CoercionLevel
{
412 // The default coercions given by the JS-API specification.
414 // Allow for the coercions given by `Spec` but also use WebAssembly.Global
415 // as a container for lossless conversions. This is only available through
416 // the wasmLosslessInvoke testing function and is used in tests.
420 // Coercion function from a JS value to a WebAssembly value [1].
422 // This function may fail for any of the following reasons:
423 // * The input value has an incorrect type for the targetType
424 // * The targetType is not exposable
426 // An error will be set upon failure.
428 // [1] https://webassembly.github.io/spec/js-api/index.html#towebassemblyvalue
429 template <typename Debug
= NoDebug
>
430 extern bool ToWebAssemblyValue(JSContext
* cx
, HandleValue val
, ValType type
,
431 void* loc
, bool mustWrite64
,
432 CoercionLevel level
= CoercionLevel::Spec
);
434 // Coercion function from a WebAssembly value to a JS value [1].
436 // This function will only fail if an OOM ocurred. If the type of WebAssembly
437 // value being coerced is not exposable to JS, then it will be coerced to
438 // 'undefined'. Callers are responsible for guarding against this if this is
441 // [1] https://webassembly.github.io/spec/js-api/index.html#tojsvalue
442 template <typename Debug
= NoDebug
>
443 extern bool ToJSValue(JSContext
* cx
, const void* src
, StorageType type
,
444 MutableHandleValue dst
,
445 CoercionLevel level
= CoercionLevel::Spec
);
446 template <typename Debug
= NoDebug
>
447 extern bool ToJSValueMayGC(StorageType type
);
448 template <typename Debug
= NoDebug
>
449 extern bool ToJSValue(JSContext
* cx
, const void* src
, ValType type
,
450 MutableHandleValue dst
,
451 CoercionLevel level
= CoercionLevel::Spec
);
452 template <typename Debug
= NoDebug
>
453 extern bool ToJSValueMayGC(ValType type
);
457 struct InternalBarrierMethods
<wasm::AnyRef
> {
458 static bool isMarkable(const wasm::AnyRef v
) { return v
.isGCThing(); }
460 static void preBarrier(const wasm::AnyRef v
) {
462 gc::PreWriteBarrierImpl(v
.toGCThing());
466 static MOZ_ALWAYS_INLINE
void postBarrier(wasm::AnyRef
* vp
,
467 const wasm::AnyRef prev
,
468 const wasm::AnyRef next
) {
469 // If the target needs an entry, add it.
471 if (next
.isGCThing() && (sb
= next
.toGCThing()->storeBuffer())) {
472 // If we know that the prev has already inserted an entry, we can
473 // skip doing the lookup to add the new entry. Note that we cannot
474 // safely assert the presence of the entry because it may have been
475 // added via a different store buffer.
476 if (prev
.isGCThing() && prev
.toGCThing()->storeBuffer()) {
479 sb
->putWasmAnyRef(vp
);
482 // Remove the prev entry if the new value does not need it.
483 if (prev
.isGCThing() && (sb
= prev
.toGCThing()->storeBuffer())) {
484 sb
->unputWasmAnyRef(vp
);
488 static void readBarrier(const wasm::AnyRef v
) {
490 gc::ReadBarrierImpl(v
.toGCThing());
495 static void assertThingIsNotGray(const wasm::AnyRef v
) {
497 JS::AssertCellIsNotGray(v
.toGCThing());
504 struct InternalBarrierMethods
<wasm::Val
> {
505 static bool isMarkable(const wasm::Val
& v
) { return v
.isAnyRef(); }
507 static void preBarrier(const wasm::Val
& v
) {
509 InternalBarrierMethods
<wasm::AnyRef
>::preBarrier(v
.toAnyRef());
513 static MOZ_ALWAYS_INLINE
void postBarrier(wasm::Val
* vp
,
514 const wasm::Val
& prev
,
515 const wasm::Val
& next
) {
516 // A wasm::Val can transition from being uninitialized to holding an anyref
517 // but cannot change kind after that.
518 MOZ_ASSERT_IF(next
.isAnyRef(), prev
.isAnyRef() || prev
.isInvalid());
519 MOZ_ASSERT_IF(prev
.isAnyRef(), next
.isAnyRef());
521 if (next
.isAnyRef()) {
522 InternalBarrierMethods
<wasm::AnyRef
>::postBarrier(
524 prev
.isAnyRef() ? prev
.toAnyRef() : wasm::AnyRef::null(),
530 static void readBarrier(const wasm::Val
& v
) {
532 InternalBarrierMethods
<wasm::AnyRef
>::readBarrier(v
.toAnyRef());
537 static void assertThingIsNotGray(const wasm::Val
& v
) {
539 InternalBarrierMethods
<wasm::AnyRef
>::assertThingIsNotGray(v
.toAnyRef());
548 struct JS::SafelyInitialized
<js::wasm::AnyRef
> {
549 static js::wasm::AnyRef
create() { return js::wasm::AnyRef::null(); }