/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * * Copyright 2023 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "wasm/WasmAnyRef.h" #include "vm/JSContext.h" #include "vm/JSObject.h" #include "vm/NativeObject.h" #include "vm/JSObject-inl.h" using namespace js; using namespace js::wasm; class WasmValueBox : public NativeObject { public: static const unsigned VALUE_SLOT = 0; static const unsigned RESERVED_SLOTS = 1; static const JSClass class_; static WasmValueBox* create(JSContext* cx, HandleValue value); Value value() const { return getFixedSlot(VALUE_SLOT); } }; const JSClass WasmValueBox::class_ = { "WasmValueBox", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS)}; WasmValueBox* WasmValueBox::create(JSContext* cx, HandleValue value) { WasmValueBox* obj = NewObjectWithGivenProto(cx, nullptr); if (!obj) { return nullptr; } obj->setFixedSlot(VALUE_SLOT, value); return obj; } const JSClass* AnyRef::valueBoxClass() { return &WasmValueBox::class_; } size_t AnyRef::valueBoxOffsetOfValue() { return NativeObject::getFixedSlotOffset(WasmValueBox::VALUE_SLOT); } bool AnyRef::fromJSValue(JSContext* cx, HandleValue value, MutableHandleAnyRef result) { if (value.isNull()) { result.set(AnyRef::null()); return true; } if (value.isString()) { JSString* string = value.toString(); result.set(AnyRef::fromJSString(string)); return true; } if (value.isObject()) { JSObject& obj = value.toObject(); MOZ_ASSERT(!obj.is()); MOZ_ASSERT(obj.compartment() == cx->compartment()); result.set(AnyRef::fromJSObject(obj)); return true; } if (value.isInt32() && !int32NeedsBoxing(value.toInt32())) { result.set(AnyRef::fromInt32(value.toInt32())); return true; } if (value.isDouble()) { double doubleValue = value.toDouble(); int32_t intValue; if (mozilla::NumberIsInt32(doubleValue, &intValue) && !int32NeedsBoxing(intValue)) { result.set(AnyRef::fromInt32(intValue)); return true; } } JSObject* box = AnyRef::boxValue(cx, value); if (!box) { return false; } result.set(AnyRef::fromJSObject(*box)); return true; } JSObject* AnyRef::boxValue(JSContext* cx, HandleValue value) { MOZ_ASSERT(AnyRef::valueNeedsBoxing(value)); return WasmValueBox::create(cx, value); } Value wasm::AnyRef::toJSValue() const { // If toJSValue needs to allocate then we need a more complicated API, and // we need to root the value in the callers, see comments in callExport(). Value value; if (isNull()) { value.setNull(); } else if (isJSString()) { value.setString(toJSString()); } else if (isI31()) { value.setInt32(toI31()); } else { JSObject& obj = toJSObject(); if (obj.is()) { value = obj.as().value(); } else { value.setObject(obj); } } return value; }