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/WasmValue.h"
20 
21 #include "jsmath.h"
22 #include "js/friend/ErrorMessages.h"  // JSMSG_*
23 #include "js/Printf.h"
24 #include "js/Value.h"
25 #include "vm/GlobalObject.h"
26 #include "vm/JSContext.h"
27 #include "vm/JSObject.h"
28 #include "vm/StringType.h"
29 #include "wasm/TypedObject.h"
30 #include "wasm/WasmJS.h"
31 
32 #include "vm/JSObject-inl.h"
33 
34 using namespace js;
35 using namespace js::wasm;
36 
Val(const LitVal & val)37 Val::Val(const LitVal& val) {
38   type_ = val.type();
39   switch (type_.kind()) {
40     case ValType::I32:
41       cell_.i32_ = val.i32();
42       return;
43     case ValType::F32:
44       cell_.f32_ = val.f32();
45       return;
46     case ValType::I64:
47       cell_.i64_ = val.i64();
48       return;
49     case ValType::F64:
50       cell_.f64_ = val.f64();
51       return;
52     case ValType::V128:
53       cell_.v128_ = val.v128();
54       return;
55     case ValType::Rtt:
56     case ValType::Ref:
57       cell_.ref_ = val.ref();
58       return;
59   }
60   MOZ_CRASH();
61 }
62 
readFromRootedLocation(const void * loc)63 void Val::readFromRootedLocation(const void* loc) {
64   memset(&cell_, 0, sizeof(Cell));
65   memcpy(&cell_, loc, type_.size());
66 }
67 
writeToRootedLocation(void * loc,bool mustWrite64) const68 void Val::writeToRootedLocation(void* loc, bool mustWrite64) const {
69   memcpy(loc, &cell_, type_.size());
70   if (mustWrite64 && type_.size() == 4) {
71     memset((uint8_t*)(loc) + 4, 0, 4);
72   }
73 }
74 
fromJSValue(JSContext * cx,ValType targetType,HandleValue val,MutableHandleVal rval)75 bool Val::fromJSValue(JSContext* cx, ValType targetType, HandleValue val,
76                       MutableHandleVal rval) {
77   rval.get().type_ = targetType;
78   // No pre/post barrier needed as rval is rooted
79   return ToWebAssemblyValue(cx, val, targetType, &rval.get().cell_,
80                             targetType.size() == 8);
81 }
82 
toJSValue(JSContext * cx,MutableHandleValue rval) const83 bool Val::toJSValue(JSContext* cx, MutableHandleValue rval) const {
84   return ToJSValue(cx, &cell_, type_, rval);
85 }
86 
trace(JSTracer * trc) const87 void Val::trace(JSTracer* trc) const {
88   if (isJSObject()) {
89     // TODO/AnyRef-boxing: With boxed immediates and strings, the write
90     // barrier is going to have to be more complicated.
91     ASSERT_ANYREF_IS_JSOBJECT;
92     TraceManuallyBarrieredEdge(trc, asJSObjectAddress(), "wasm val");
93   }
94 }
95 
CheckRefType(JSContext * cx,RefType targetType,HandleValue v,MutableHandleFunction fnval,MutableHandleAnyRef refval)96 bool wasm::CheckRefType(JSContext* cx, RefType targetType, HandleValue v,
97                         MutableHandleFunction fnval,
98                         MutableHandleAnyRef refval) {
99   if (!targetType.isNullable() && v.isNull()) {
100     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
101                              JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE);
102     return false;
103   }
104   switch (targetType.kind()) {
105     case RefType::Func:
106       if (!CheckFuncRefValue(cx, v, fnval)) {
107         return false;
108       }
109       break;
110     case RefType::Extern:
111       if (!BoxAnyRef(cx, v, refval)) {
112         return false;
113       }
114       break;
115     case RefType::Eq:
116       if (!CheckEqRefValue(cx, v, refval)) {
117         return false;
118       }
119       break;
120     case RefType::TypeIndex:
121       MOZ_CRASH("temporarily unsupported Ref type");
122   }
123   return true;
124 }
125 
CheckFuncRefValue(JSContext * cx,HandleValue v,MutableHandleFunction fun)126 bool wasm::CheckFuncRefValue(JSContext* cx, HandleValue v,
127                              MutableHandleFunction fun) {
128   if (v.isNull()) {
129     MOZ_ASSERT(!fun);
130     return true;
131   }
132 
133   if (v.isObject()) {
134     JSObject& obj = v.toObject();
135     if (obj.is<JSFunction>()) {
136       JSFunction* f = &obj.as<JSFunction>();
137       if (IsWasmExportedFunction(f)) {
138         fun.set(f);
139         return true;
140       }
141     }
142   }
143 
144   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
145                            JSMSG_WASM_BAD_FUNCREF_VALUE);
146   return false;
147 }
148 
CheckEqRefValue(JSContext * cx,HandleValue v,MutableHandleAnyRef vp)149 bool wasm::CheckEqRefValue(JSContext* cx, HandleValue v,
150                            MutableHandleAnyRef vp) {
151   if (v.isNull()) {
152     vp.set(AnyRef::null());
153     return true;
154   }
155 
156   if (v.isObject()) {
157     JSObject& obj = v.toObject();
158     if (obj.is<TypedObject>()) {
159       vp.set(AnyRef::fromJSObject(&obj.as<TypedObject>()));
160       return true;
161     }
162   }
163 
164   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
165                            JSMSG_WASM_BAD_EQREF_VALUE);
166   return false;
167 }
168 
169 class wasm::NoDebug {
170  public:
171   template <typename T>
print(T v)172   static void print(T v) {}
173 };
174 
175 class wasm::DebugCodegenVal {
176   template <typename T>
print(const char * fmt,T v)177   static void print(const char* fmt, T v) {
178     DebugCodegen(DebugChannel::Function, fmt, v);
179   }
180 
181  public:
print(int32_t v)182   static void print(int32_t v) { print(" i32(%d)", v); }
print(int64_t v)183   static void print(int64_t v) { print(" i64(%" PRId64 ")", v); }
print(float v)184   static void print(float v) { print(" f32(%f)", v); }
print(double v)185   static void print(double v) { print(" f64(%lf)", v); }
print(void * v)186   static void print(void* v) { print(" ptr(%p)", v); }
187 };
188 
189 template bool wasm::ToWebAssemblyValue<NoDebug>(JSContext* cx, HandleValue val,
190                                                 FieldType type, void* loc,
191                                                 bool mustWrite64,
192                                                 CoercionLevel level);
193 template bool wasm::ToWebAssemblyValue<DebugCodegenVal>(
194     JSContext* cx, HandleValue val, FieldType type, void* loc, bool mustWrite64,
195     CoercionLevel level);
196 template bool wasm::ToJSValue<NoDebug>(JSContext* cx, const void* src,
197                                        FieldType type, MutableHandleValue dst,
198                                        CoercionLevel level);
199 template bool wasm::ToJSValue<DebugCodegenVal>(JSContext* cx, const void* src,
200                                                FieldType type,
201                                                MutableHandleValue dst,
202                                                CoercionLevel level);
203 
204 template bool wasm::ToWebAssemblyValue<NoDebug>(JSContext* cx, HandleValue val,
205                                                 ValType type, void* loc,
206                                                 bool mustWrite64,
207                                                 CoercionLevel level);
208 template bool wasm::ToWebAssemblyValue<DebugCodegenVal>(JSContext* cx,
209                                                         HandleValue val,
210                                                         ValType type, void* loc,
211                                                         bool mustWrite64,
212                                                         CoercionLevel level);
213 template bool wasm::ToJSValue<NoDebug>(JSContext* cx, const void* src,
214                                        ValType type, MutableHandleValue dst,
215                                        CoercionLevel level);
216 template bool wasm::ToJSValue<DebugCodegenVal>(JSContext* cx, const void* src,
217                                                ValType type,
218                                                MutableHandleValue dst,
219                                                CoercionLevel level);
220 
221 template <typename Debug = NoDebug>
ToWebAssemblyValue_i8(JSContext * cx,HandleValue val,int8_t * loc)222 bool ToWebAssemblyValue_i8(JSContext* cx, HandleValue val, int8_t* loc) {
223   bool ok = ToInt8(cx, val, loc);
224   Debug::print(*loc);
225   return ok;
226 }
227 template <typename Debug = NoDebug>
ToWebAssemblyValue_i16(JSContext * cx,HandleValue val,int16_t * loc)228 bool ToWebAssemblyValue_i16(JSContext* cx, HandleValue val, int16_t* loc) {
229   bool ok = ToInt16(cx, val, loc);
230   Debug::print(*loc);
231   return ok;
232 }
233 template <typename Debug = NoDebug>
ToWebAssemblyValue_i32(JSContext * cx,HandleValue val,int32_t * loc,bool mustWrite64)234 bool ToWebAssemblyValue_i32(JSContext* cx, HandleValue val, int32_t* loc,
235                             bool mustWrite64) {
236   bool ok = ToInt32(cx, val, loc);
237   if (ok && mustWrite64) {
238 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
239     loc[1] = loc[0] >> 31;
240 #else
241     loc[1] = 0;
242 #endif
243   }
244   Debug::print(*loc);
245   return ok;
246 }
247 template <typename Debug = NoDebug>
ToWebAssemblyValue_i64(JSContext * cx,HandleValue val,int64_t * loc,bool mustWrite64)248 bool ToWebAssemblyValue_i64(JSContext* cx, HandleValue val, int64_t* loc,
249                             bool mustWrite64) {
250   MOZ_ASSERT(mustWrite64);
251   JS_TRY_VAR_OR_RETURN_FALSE(cx, *loc, ToBigInt64(cx, val));
252   Debug::print(*loc);
253   return true;
254 }
255 template <typename Debug = NoDebug>
ToWebAssemblyValue_f32(JSContext * cx,HandleValue val,float * loc,bool mustWrite64)256 bool ToWebAssemblyValue_f32(JSContext* cx, HandleValue val, float* loc,
257                             bool mustWrite64) {
258   bool ok = RoundFloat32(cx, val, loc);
259   if (ok && mustWrite64) {
260     loc[1] = 0.0;
261   }
262   Debug::print(*loc);
263   return ok;
264 }
265 template <typename Debug = NoDebug>
ToWebAssemblyValue_f64(JSContext * cx,HandleValue val,double * loc,bool mustWrite64)266 bool ToWebAssemblyValue_f64(JSContext* cx, HandleValue val, double* loc,
267                             bool mustWrite64) {
268   MOZ_ASSERT(mustWrite64);
269   bool ok = ToNumber(cx, val, loc);
270   Debug::print(*loc);
271   return ok;
272 }
273 template <typename Debug = NoDebug>
ToWebAssemblyValue_externref(JSContext * cx,HandleValue val,void ** loc,bool mustWrite64)274 bool ToWebAssemblyValue_externref(JSContext* cx, HandleValue val, void** loc,
275                                   bool mustWrite64) {
276   RootedAnyRef result(cx, AnyRef::null());
277   if (!BoxAnyRef(cx, val, &result)) {
278     return false;
279   }
280   *loc = result.get().forCompiledCode();
281 #ifndef JS_64BIT
282   if (mustWrite64) {
283     loc[1] = nullptr;
284   }
285 #endif
286   Debug::print(*loc);
287   return true;
288 }
289 template <typename Debug = NoDebug>
ToWebAssemblyValue_eqref(JSContext * cx,HandleValue val,void ** loc,bool mustWrite64)290 bool ToWebAssemblyValue_eqref(JSContext* cx, HandleValue val, void** loc,
291                               bool mustWrite64) {
292   RootedAnyRef result(cx, AnyRef::null());
293   if (!CheckEqRefValue(cx, val, &result)) {
294     return false;
295   }
296   *loc = result.get().forCompiledCode();
297 #ifndef JS_64BIT
298   if (mustWrite64) {
299     loc[1] = nullptr;
300   }
301 #endif
302   Debug::print(*loc);
303   return true;
304 }
305 template <typename Debug = NoDebug>
ToWebAssemblyValue_funcref(JSContext * cx,HandleValue val,void ** loc,bool mustWrite64)306 bool ToWebAssemblyValue_funcref(JSContext* cx, HandleValue val, void** loc,
307                                 bool mustWrite64) {
308   RootedFunction fun(cx);
309   if (!CheckFuncRefValue(cx, val, &fun)) {
310     return false;
311   }
312   *loc = fun;
313 #ifndef JS_64BIT
314   if (mustWrite64) {
315     loc[1] = nullptr;
316   }
317 #endif
318   Debug::print(*loc);
319   return true;
320 }
321 
ToWebAssemblyValue_lossless(JSContext * cx,HandleValue val,ValType type,void * loc,bool mustWrite64)322 bool ToWebAssemblyValue_lossless(JSContext* cx, HandleValue val, ValType type,
323                                  void* loc, bool mustWrite64) {
324   if (!val.isObject() || !val.toObject().is<WasmGlobalObject>()) {
325     return false;
326   }
327   Rooted<WasmGlobalObject*> srcVal(cx, &val.toObject().as<WasmGlobalObject>());
328 
329   if (srcVal->type() != type) {
330     return false;
331   }
332 
333   srcVal->val().get().writeToRootedLocation(loc, mustWrite64);
334   return true;
335 }
336 
337 template <typename Debug>
ToWebAssemblyValue(JSContext * cx,HandleValue val,FieldType type,void * loc,bool mustWrite64,CoercionLevel level)338 bool wasm::ToWebAssemblyValue(JSContext* cx, HandleValue val, FieldType type,
339                               void* loc, bool mustWrite64,
340                               CoercionLevel level) {
341   if (level == CoercionLevel::Lossless &&
342       ToWebAssemblyValue_lossless(cx, val, type.valType(), (void*)loc,
343                                   mustWrite64)) {
344     return true;
345   }
346 
347   switch (type.kind()) {
348     case FieldType::I8:
349       return ToWebAssemblyValue_i8<Debug>(cx, val, (int8_t*)loc);
350     case FieldType::I16:
351       return ToWebAssemblyValue_i16<Debug>(cx, val, (int16_t*)loc);
352     case FieldType::I32:
353       return ToWebAssemblyValue_i32<Debug>(cx, val, (int32_t*)loc, mustWrite64);
354     case FieldType::I64:
355       return ToWebAssemblyValue_i64<Debug>(cx, val, (int64_t*)loc, mustWrite64);
356     case FieldType::F32:
357       return ToWebAssemblyValue_f32<Debug>(cx, val, (float*)loc, mustWrite64);
358     case FieldType::F64:
359       return ToWebAssemblyValue_f64<Debug>(cx, val, (double*)loc, mustWrite64);
360     case FieldType::V128:
361       break;
362     case FieldType::Rtt:
363       break;
364     case FieldType::Ref:
365 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
366       if (!type.isNullable() && val.isNull()) {
367         JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
368                                  JSMSG_WASM_BAD_REF_NONNULLABLE_VALUE);
369         return false;
370       }
371 #else
372       MOZ_ASSERT(type.isNullable());
373 #endif
374       switch (type.refTypeKind()) {
375         case RefType::Func:
376           return ToWebAssemblyValue_funcref<Debug>(cx, val, (void**)loc,
377                                                    mustWrite64);
378         case RefType::Extern:
379           return ToWebAssemblyValue_externref<Debug>(cx, val, (void**)loc,
380                                                      mustWrite64);
381         case RefType::Eq:
382           return ToWebAssemblyValue_eqref<Debug>(cx, val, (void**)loc,
383                                                  mustWrite64);
384         case RefType::TypeIndex:
385           break;
386       }
387   }
388 
389   MOZ_ASSERT(!type.isExposable());
390   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
391                            JSMSG_WASM_BAD_VAL_TYPE);
392   return false;
393 }
394 template <typename Debug>
ToWebAssemblyValue(JSContext * cx,HandleValue val,ValType type,void * loc,bool mustWrite64,CoercionLevel level)395 bool wasm::ToWebAssemblyValue(JSContext* cx, HandleValue val, ValType type,
396                               void* loc, bool mustWrite64,
397                               CoercionLevel level) {
398   return wasm::ToWebAssemblyValue(cx, val, FieldType(type.packed()), loc,
399                                   mustWrite64, level);
400 }
401 
402 template <typename Debug = NoDebug>
ToJSValue_i8(JSContext * cx,int8_t src,MutableHandleValue dst)403 bool ToJSValue_i8(JSContext* cx, int8_t src, MutableHandleValue dst) {
404   dst.set(Int32Value(src));
405   Debug::print(src);
406   return true;
407 }
408 template <typename Debug = NoDebug>
ToJSValue_i16(JSContext * cx,int16_t src,MutableHandleValue dst)409 bool ToJSValue_i16(JSContext* cx, int16_t src, MutableHandleValue dst) {
410   dst.set(Int32Value(src));
411   Debug::print(src);
412   return true;
413 }
414 template <typename Debug = NoDebug>
ToJSValue_i32(JSContext * cx,int32_t src,MutableHandleValue dst)415 bool ToJSValue_i32(JSContext* cx, int32_t src, MutableHandleValue dst) {
416   dst.set(Int32Value(src));
417   Debug::print(src);
418   return true;
419 }
420 template <typename Debug = NoDebug>
ToJSValue_i64(JSContext * cx,int64_t src,MutableHandleValue dst)421 bool ToJSValue_i64(JSContext* cx, int64_t src, MutableHandleValue dst) {
422   // If bi is manipulated other than test & storing, it would need
423   // to be rooted here.
424   BigInt* bi = BigInt::createFromInt64(cx, src);
425   if (!bi) {
426     return false;
427   }
428   dst.set(BigIntValue(bi));
429   Debug::print(src);
430   return true;
431 }
432 template <typename Debug = NoDebug>
ToJSValue_f32(JSContext * cx,float src,MutableHandleValue dst)433 bool ToJSValue_f32(JSContext* cx, float src, MutableHandleValue dst) {
434   dst.set(JS::CanonicalizedDoubleValue(src));
435   Debug::print(src);
436   return true;
437 }
438 template <typename Debug = NoDebug>
ToJSValue_f64(JSContext * cx,double src,MutableHandleValue dst)439 bool ToJSValue_f64(JSContext* cx, double src, MutableHandleValue dst) {
440   dst.set(JS::CanonicalizedDoubleValue(src));
441   Debug::print(src);
442   return true;
443 }
444 template <typename Debug = NoDebug>
ToJSValue_funcref(JSContext * cx,void * src,MutableHandleValue dst)445 bool ToJSValue_funcref(JSContext* cx, void* src, MutableHandleValue dst) {
446   dst.set(UnboxFuncRef(FuncRef::fromCompiledCode(src)));
447   Debug::print(src);
448   return true;
449 }
450 template <typename Debug = NoDebug>
ToJSValue_anyref(JSContext * cx,void * src,MutableHandleValue dst)451 bool ToJSValue_anyref(JSContext* cx, void* src, MutableHandleValue dst) {
452   dst.set(UnboxAnyRef(AnyRef::fromCompiledCode(src)));
453   Debug::print(src);
454   return true;
455 }
456 
457 template <typename Debug = NoDebug>
ToJSValue_lossless(JSContext * cx,const void * src,MutableHandleValue dst,ValType type)458 bool ToJSValue_lossless(JSContext* cx, const void* src, MutableHandleValue dst,
459                         ValType type) {
460   RootedVal srcVal(cx, type);
461   srcVal.get().readFromRootedLocation(src);
462   RootedObject prototype(
463       cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
464   Rooted<WasmGlobalObject*> srcGlobal(
465       cx, WasmGlobalObject::create(cx, srcVal, false, prototype));
466   dst.set(ObjectValue(*srcGlobal.get()));
467   return true;
468 }
469 
470 template <typename Debug>
ToJSValue(JSContext * cx,const void * src,FieldType type,MutableHandleValue dst,CoercionLevel level)471 bool wasm::ToJSValue(JSContext* cx, const void* src, FieldType type,
472                      MutableHandleValue dst, CoercionLevel level) {
473   if (level == CoercionLevel::Lossless) {
474     MOZ_ASSERT(type.isValType());
475     return ToJSValue_lossless(cx, src, dst, type.valType());
476   }
477 
478   switch (type.kind()) {
479     case FieldType::I8:
480       return ToJSValue_i8<Debug>(cx, *reinterpret_cast<const int8_t*>(src),
481                                  dst);
482     case FieldType::I16:
483       return ToJSValue_i16<Debug>(cx, *reinterpret_cast<const int16_t*>(src),
484                                   dst);
485     case FieldType::I32:
486       return ToJSValue_i32<Debug>(cx, *reinterpret_cast<const int32_t*>(src),
487                                   dst);
488     case FieldType::I64:
489       return ToJSValue_i64<Debug>(cx, *reinterpret_cast<const int64_t*>(src),
490                                   dst);
491     case FieldType::F32:
492       return ToJSValue_f32<Debug>(cx, *reinterpret_cast<const float*>(src),
493                                   dst);
494     case FieldType::F64:
495       return ToJSValue_f64<Debug>(cx, *reinterpret_cast<const double*>(src),
496                                   dst);
497     case FieldType::V128:
498       break;
499     case FieldType::Rtt:
500       break;
501     case FieldType::Ref:
502       switch (type.refTypeKind()) {
503         case RefType::Func:
504           return ToJSValue_funcref<Debug>(
505               cx, *reinterpret_cast<void* const*>(src), dst);
506         case RefType::Extern:
507           return ToJSValue_anyref<Debug>(
508               cx, *reinterpret_cast<void* const*>(src), dst);
509         case RefType::Eq:
510           return ToJSValue_anyref<Debug>(
511               cx, *reinterpret_cast<void* const*>(src), dst);
512         case RefType::TypeIndex:
513           break;
514       }
515   }
516   MOZ_ASSERT(!type.isExposable());
517   Debug::print(nullptr);
518   dst.setUndefined();
519   return true;
520 }
521 template <typename Debug>
ToJSValue(JSContext * cx,const void * src,ValType type,MutableHandleValue dst,CoercionLevel level)522 bool wasm::ToJSValue(JSContext* cx, const void* src, ValType type,
523                      MutableHandleValue dst, CoercionLevel level) {
524   return wasm::ToJSValue(cx, src, FieldType(type.packed()), dst, level);
525 }
526 
trace(JSTracer * trc)527 void AnyRef::trace(JSTracer* trc) {
528   if (value_) {
529     TraceManuallyBarrieredEdge(trc, &value_, "wasm anyref referent");
530   }
531 }
532 
533 const JSClass WasmValueBox::class_ = {
534     "WasmValueBox", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS)};
535 
create(JSContext * cx,HandleValue val)536 WasmValueBox* WasmValueBox::create(JSContext* cx, HandleValue val) {
537   WasmValueBox* obj = NewObjectWithGivenProto<WasmValueBox>(cx, nullptr);
538   if (!obj) {
539     return nullptr;
540   }
541   obj->setFixedSlot(VALUE_SLOT, val);
542   return obj;
543 }
544 
BoxAnyRef(JSContext * cx,HandleValue val,MutableHandleAnyRef result)545 bool wasm::BoxAnyRef(JSContext* cx, HandleValue val,
546                      MutableHandleAnyRef result) {
547   if (val.isNull()) {
548     result.set(AnyRef::null());
549     return true;
550   }
551 
552   if (val.isObject()) {
553     JSObject* obj = &val.toObject();
554     MOZ_ASSERT(!obj->is<WasmValueBox>());
555     MOZ_ASSERT(obj->compartment() == cx->compartment());
556     result.set(AnyRef::fromJSObject(obj));
557     return true;
558   }
559 
560   WasmValueBox* box = WasmValueBox::create(cx, val);
561   if (!box) return false;
562   result.set(AnyRef::fromJSObject(box));
563   return true;
564 }
565 
BoxBoxableValue(JSContext * cx,HandleValue val)566 JSObject* wasm::BoxBoxableValue(JSContext* cx, HandleValue val) {
567   MOZ_ASSERT(!val.isNull() && !val.isObject());
568   return WasmValueBox::create(cx, val);
569 }
570 
UnboxAnyRef(AnyRef val)571 Value wasm::UnboxAnyRef(AnyRef val) {
572   // If UnboxAnyRef needs to allocate then we need a more complicated API, and
573   // we need to root the value in the callers, see comments in callExport().
574   JSObject* obj = val.asJSObject();
575   Value result;
576   if (obj == nullptr) {
577     result.setNull();
578   } else if (obj->is<WasmValueBox>()) {
579     result = obj->as<WasmValueBox>().value();
580   } else {
581     result.setObjectOrNull(obj);
582   }
583   return result;
584 }
585 
586 /* static */
fromAnyRefUnchecked(AnyRef p)587 wasm::FuncRef wasm::FuncRef::fromAnyRefUnchecked(AnyRef p) {
588 #ifdef DEBUG
589   Value v = UnboxAnyRef(p);
590   if (v.isNull()) {
591     return FuncRef(nullptr);
592   }
593   if (v.toObject().is<JSFunction>()) {
594     return FuncRef(&v.toObject().as<JSFunction>());
595   }
596   MOZ_CRASH("Bad value");
597 #else
598   return FuncRef(&p.asJSObject()->as<JSFunction>());
599 #endif
600 }
601 
UnboxFuncRef(FuncRef val)602 Value wasm::UnboxFuncRef(FuncRef val) {
603   JSFunction* fn = val.asJSFunction();
604   Value result;
605   MOZ_ASSERT_IF(fn, fn->is<JSFunction>());
606   result.setObjectOrNull(fn);
607   return result;
608 }
609