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