bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) { if (!cx->compartment()->wasm.ensureProfilingState(cx)) return false; const FuncExport& func = metadata().lookupFuncExport(funcIndex); // The calling convention for an external call into wasm is to pass an // array of 16-byte values where each value contains either a coerced int32 // (in the low word), a double value (in the low dword) or a SIMD vector // value, with the coercions specified by the wasm signature. The external // entry point unpacks this array into the system-ABI-specified registers // and stack memory and then calls into the internal entry point. The return // value is stored in the first element of the array (which, therefore, must // have length >= 1). Vector<ExportArg, 8> exportArgs(cx); if (!exportArgs.resize(Max<size_t>(1, func.sig().args().length()))) return false; RootedValue v(cx); for (unsigned i = 0; i < func.sig().args().length(); ++i) { v = i < args.length() ? args[i] : UndefinedValue(); switch (func.sig().arg(i)) { case ValType::I32: if (!ToInt32(cx, v, (int32_t*)&exportArgs[i])) return false; break; case ValType::I64: if (!JitOptions.wasmTestMode) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } if (!ReadI64Object(cx, v, (int64_t*)&exportArgs[i])) return false; break; case ValType::F32: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomFloat32NaNObject(cx, v, (float*)&exportArgs[i])) return false; break; } if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) return false; break; case ValType::F64: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomDoubleNaNObject(cx, v, (double*)&exportArgs[i])) return false; break; } if (!ToNumber(cx, v, (double*)&exportArgs[i])) return false; break; case ValType::I8x16: { SimdConstant simd; if (!ToSimdConstant<Int8x16>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize); break; } case ValType::I16x8: { SimdConstant simd; if (!ToSimdConstant<Int16x8>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize); break; } case ValType::I32x4: { SimdConstant simd; if (!ToSimdConstant<Int32x4>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::F32x4: { SimdConstant simd; if (!ToSimdConstant<Float32x4>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asFloat32x4(), Simd128DataSize); break; } case ValType::B8x16: { SimdConstant simd; if (!ToSimdConstant<Bool8x16>(cx, v, &simd)) return false; // Bool8x16 uses the same representation as Int8x16. memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize); break; } case ValType::B16x8: { SimdConstant simd; if (!ToSimdConstant<Bool16x8>(cx, v, &simd)) return false; // Bool16x8 uses the same representation as Int16x8. memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize); break; } case ValType::B32x4: { SimdConstant simd; if (!ToSimdConstant<Bool32x4>(cx, v, &simd)) return false; // Bool32x4 uses the same representation as Int32x4. memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::Limit: MOZ_CRASH("Limit"); } } { // Push a WasmActivation to describe the wasm frames we're about to push // when running this module. Additionally, push a JitActivation so that // the optimized wasm-to-Ion FFI call path (which we want to be very // fast) can avoid doing so. The JitActivation is marked as inactive so // stack iteration will skip over it. WasmActivation activation(cx); JitActivation jitActivation(cx, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase() + func.entryOffset()); if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), &tlsData_)) return false; } if (args.isConstructing()) { // By spec, when a function is called as a constructor and this function // returns a primary type, which is the case for all wasm exported // functions, the returned value is discarded and an empty object is // returned instead. PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx); if (!obj) return false; args.rval().set(ObjectValue(*obj)); return true; } void* retAddr = &exportArgs[0]; JSObject* retObj = nullptr; switch (func.sig().ret()) { case ExprType::Void: args.rval().set(UndefinedValue()); break; case ExprType::I32: args.rval().set(Int32Value(*(int32_t*)retAddr)); break; case ExprType::I64: if (!JitOptions.wasmTestMode) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } retObj = CreateI64Object(cx, *(int64_t*)retAddr); if (!retObj) return false; break; case ExprType::F32: if (JitOptions.wasmTestMode && IsNaN(*(float*)retAddr)) { retObj = CreateCustomNaNObject(cx, (float*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(float*)retAddr)); break; case ExprType::F64: if (JitOptions.wasmTestMode && IsNaN(*(double*)retAddr)) { retObj = CreateCustomNaNObject(cx, (double*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(double*)retAddr)); break; case ExprType::I8x16: retObj = CreateSimd<Int8x16>(cx, (int8_t*)retAddr); if (!retObj) return false; break; case ExprType::I16x8: retObj = CreateSimd<Int16x8>(cx, (int16_t*)retAddr); if (!retObj) return false; break; case ExprType::I32x4: retObj = CreateSimd<Int32x4>(cx, (int32_t*)retAddr); if (!retObj) return false; break; case ExprType::F32x4: retObj = CreateSimd<Float32x4>(cx, (float*)retAddr); if (!retObj) return false; break; case ExprType::B8x16: retObj = CreateSimd<Bool8x16>(cx, (int8_t*)retAddr); if (!retObj) return false; break; case ExprType::B16x8: retObj = CreateSimd<Bool16x8>(cx, (int16_t*)retAddr); if (!retObj) return false; break; case ExprType::B32x4: retObj = CreateSimd<Bool32x4>(cx, (int32_t*)retAddr); if (!retObj) return false; break; case ExprType::Limit: MOZ_CRASH("Limit"); } if (retObj) args.rval().set(ObjectValue(*retObj)); return true; }