| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLibraryPch.h"
- #ifdef ENABLE_WASM
- #include "../WasmReader/WasmReaderPch.h"
- namespace Js
- {
- Var GetImportVariable(Wasm::WasmImport* wi, ScriptContext* ctx, Var ffi)
- {
- PropertyRecord const * modPropertyRecord = nullptr;
- const char16* modName = wi->modName;
- uint32 modNameLen = wi->modNameLen;
- ctx->GetOrAddPropertyRecord(modName, modNameLen, &modPropertyRecord);
- Var modProp = JavascriptOperators::OP_GetProperty(ffi, modPropertyRecord->GetPropertyId(), ctx);
- if (!JavascriptOperators::IsObject(modProp))
- {
- JavascriptError::ThrowTypeErrorVar(ctx, WASMERR_InvalidImportModule, modName);
- }
- const char16* name = wi->importName;
- uint32 nameLen = wi->importNameLen;
- PropertyRecord const * propertyRecord = nullptr;
- ctx->GetOrAddPropertyRecord(name, nameLen, &propertyRecord);
- if (!JavascriptOperators::IsObject(modProp))
- {
- JavascriptError::ThrowTypeErrorVar(ctx, WASMERR_InvalidImport, modName, name, _u("Object"));
- }
- return JavascriptOperators::OP_GetProperty(modProp, propertyRecord->GetPropertyId(), ctx);
- }
- WebAssemblyInstance::WebAssemblyInstance(WebAssemblyModule * wasmModule, DynamicType * type) :
- DynamicObject(type),
- m_module(wasmModule),
- m_exports(nullptr)
- {
- }
- /* static */
- bool
- WebAssemblyInstance::Is(Var value)
- {
- return JavascriptOperators::GetTypeId(value) == TypeIds_WebAssemblyInstance;
- }
- /* static */
- WebAssemblyInstance *
- WebAssemblyInstance::FromVar(Var value)
- {
- AssertOrFailFast(WebAssemblyInstance::Is(value));
- return static_cast<WebAssemblyInstance*>(value);
- }
- /* static */
- WebAssemblyInstance *
- WebAssemblyInstance::UnsafeFromVar(Var value)
- {
- Assert(WebAssemblyInstance::Is(value));
- return static_cast<WebAssemblyInstance*>(value);
- }
- // Implements "new WebAssembly.Instance(moduleObject [, importObject])" as described here:
- // https://github.com/WebAssembly/design/blob/master/JS.md#webassemblyinstance-constructor
- Var
- WebAssemblyInstance::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
- Var newTarget = args.GetNewTarget();
- JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
- if (!args.IsNewCall() || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
- {
- JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("WebAssembly.Instance"));
- }
- if (args.Info.Count < 2 || !WebAssemblyModule::Is(args[1]))
- {
- JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedModule);
- }
- WebAssemblyModule * module = WebAssemblyModule::FromVar(args[1]);
- Var importObject = scriptContext->GetLibrary()->GetUndefined();
- if (args.Info.Count >= 3)
- {
- importObject = args[2];
- }
- return CreateInstance(module, importObject);
- }
- Var
- WebAssemblyInstance::GetterExports(RecyclableObject* function, CallInfo callInfo, ...)
- {
- PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
- ARGUMENTS(args, callInfo);
- ScriptContext* scriptContext = function->GetScriptContext();
- Assert(!(callInfo.Flags & CallFlags_New));
- if (args.Info.Count == 0 || !WebAssemblyInstance::Is(args[0]))
- {
- JavascriptError::ThrowTypeError(scriptContext, WASMERR_NeedInstanceObject);
- }
- WebAssemblyInstance* instance = WebAssemblyInstance::FromVar(args[0]);
- Js::Var exports = instance->m_exports;
- if (!exports || !DynamicObject::Is(exports))
- {
- Assert(UNREACHED);
- exports = scriptContext->GetLibrary()->GetUndefined();
- }
- return exports;
- }
- WebAssemblyInstance *
- WebAssemblyInstance::CreateInstance(WebAssemblyModule * module, Var importObject)
- {
- if (!JavascriptOperators::IsUndefined(importObject) && !JavascriptOperators::IsObject(importObject))
- {
- JavascriptError::ThrowTypeError(module->GetScriptContext(), JSERR_NeedObject);
- }
- ScriptContext * scriptContext = module->GetScriptContext();
- WebAssemblyEnvironment environment(module);
- WebAssemblyInstance * newInstance = RecyclerNewZ(scriptContext->GetRecycler(), WebAssemblyInstance, module, scriptContext->GetLibrary()->GetWebAssemblyInstanceType());
- try
- {
- LoadImports(module, scriptContext, importObject, &environment);
- ValidateTableAndMemory(module, scriptContext, &environment);
- InitialGlobals(module, scriptContext, &environment);
- CreateWasmFunctions(module, scriptContext, &environment);
- try
- {
- InitializeDataSegs(module, scriptContext, &environment);
- InitializeFunctionTable(module, scriptContext, &environment);
- }
- catch (...)
- {
- AssertMsg(UNREACHED, "By spec, we should not have any exceptions possible here");
- throw;
- }
- newInstance->m_exports = CreateExportObject(module, scriptContext, &environment);
- }
- catch (Wasm::WasmCompilationException& e)
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(scriptContext, WASMERR_WasmLinkError, e.GetTempErrorMessageRef());
- }
- uint32 startFuncIdx = module->GetStartFunction();
- if (startFuncIdx != Js::Constants::UninitializedValue)
- {
- WasmScriptFunction* start = environment.GetWasmFunction(startFuncIdx);
- Js::CallInfo info(Js::CallFlags_New, 1);
- Js::Arguments startArg(info, (Var*)&start);
- Js::JavascriptFunction::CallFunction<true>(start, start->GetEntryPoint(), startArg);
- }
- return newInstance;
- }
- void WebAssemblyInstance::CreateWasmFunctions(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env)
- {
- FrameDisplay * frameDisplay = RecyclerNewPlus(ctx->GetRecycler(), sizeof(void*), FrameDisplay, 0);
- for (uint i = 0; i < wasmModule->GetWasmFunctionCount(); ++i)
- {
- if (i < wasmModule->GetImportedFunctionCount() && env->GetWasmFunction(i) != nullptr)
- {
- continue;
- }
- Wasm::WasmFunctionInfo* wasmFuncInfo = wasmModule->GetWasmFunctionInfo(i);
- FunctionBody* body = wasmFuncInfo->GetBody();
- WasmScriptFunction* funcObj = ctx->GetLibrary()->CreateWasmScriptFunction(body);
- funcObj->SetModuleEnvironment(env->GetStartPtr());
- funcObj->SetSignature(body->GetAsmJsFunctionInfo()->GetWasmSignature());
- funcObj->SetEnvironment(frameDisplay);
- // Todo:: need to fix issue #2452 before we can do this,
- // otherwise we'll change the type of the functions and cause multiple instance to not share jitted code
- //Wasm::WasmSignature* sig = wasmFuncInfo->GetSignature();
- //funcObj->SetPropertyWithAttributes(PropertyIds::length, JavascriptNumber::ToVar(sig->GetParamCount(), ctx), PropertyNone, nullptr);
- //funcObj->SetPropertyWithAttributes(PropertyIds::name, JavascriptConversion::ToString(JavascriptNumber::ToVar(i, ctx), ctx), PropertyNone, nullptr);
- env->SetWasmFunction(i, funcObj);
- if (!PHASE_OFF(WasmDeferredPhase, body))
- {
- // if we still have WasmReaderInfo we haven't yet parsed
- if (body->GetAsmJsFunctionInfo()->GetWasmReaderInfo())
- {
- WasmLibrary::SetWasmEntryPointToInterpreter(funcObj, true);
- }
- }
- else
- {
- AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
- if (info->GetWasmReaderInfo())
- {
- WasmLibrary::SetWasmEntryPointToInterpreter(funcObj, false);
- WAsmJs::JitFunctionIfReady(funcObj);
- info->SetWasmReaderInfo(nullptr);
- }
- }
- }
- }
- void WebAssemblyInstance::InitializeDataSegs(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env)
- {
- WebAssemblyMemory* mem = env->GetMemory(0);
- Assert(mem);
- ArrayBuffer* buffer = mem->GetBuffer();
- for (uint32 iSeg = 0; iSeg < wasmModule->GetDataSegCount(); ++iSeg)
- {
- Wasm::WasmDataSegment* segment = wasmModule->GetDataSeg(iSeg);
- Assert(segment != nullptr);
- const uint32 offset = env->GetDataSegmentOffset(iSeg);
- const uint32 size = segment->GetSourceSize();
- if (size > 0)
- {
- js_memcpy_s(buffer->GetBuffer() + offset, (uint32)buffer->GetByteLength() - offset, segment->GetData(), size);
- }
- }
- }
- Var WebAssemblyInstance::CreateExportObject(WebAssemblyModule * wasmModule, ScriptContext* scriptContext, WebAssemblyEnvironment* env)
- {
- Js::Var exportsNamespace = scriptContext->GetLibrary()->CreateObject(scriptContext->GetLibrary()->GetNull());
- for (uint32 iExport = 0; iExport < wasmModule->GetExportCount(); ++iExport)
- {
- Wasm::WasmExport* wasmExport = wasmModule->GetExport(iExport);
- Assert(wasmExport);
- if (wasmExport)
- {
- PropertyRecord const * propertyRecord = nullptr;
- scriptContext->GetOrAddPropertyRecord(wasmExport->name, wasmExport->nameLength, &propertyRecord);
- Var obj = scriptContext->GetLibrary()->GetUndefined();
- switch (wasmExport->kind)
- {
- case Wasm::ExternalKinds::Table:
- obj = env->GetTable(wasmExport->index);
- break;
- case Wasm::ExternalKinds::Memory:
- obj = env->GetMemory(wasmExport->index);
- break;
- case Wasm::ExternalKinds::Function:
- obj = env->GetWasmFunction(wasmExport->index);
- break;
- case Wasm::ExternalKinds::Global:
- Wasm::WasmGlobal* global = wasmModule->GetGlobal(wasmExport->index);
- if (global->IsMutable())
- {
- JavascriptError::ThrowTypeError(wasmModule->GetScriptContext(), WASMERR_MutableGlobal);
- }
- Wasm::WasmConstLitNode cnst = env->GetGlobalValue(global);
- switch (global->GetType())
- {
- case Wasm::WasmTypes::I32:
- obj = JavascriptNumber::ToVar(cnst.i32, scriptContext);
- break;
- case Wasm::WasmTypes::F32:
- obj = JavascriptNumber::New(cnst.f32, scriptContext);
- break;
- case Wasm::WasmTypes::F64:
- obj = JavascriptNumber::New(cnst.f64, scriptContext);
- break;
- case Wasm::WasmTypes::I64:
- JavascriptError::ThrowTypeError(wasmModule->GetScriptContext(), WASMERR_InvalidTypeConversion);
- default:
- Assert(UNREACHED);
- break;
- }
- }
- JavascriptOperators::OP_SetProperty(exportsNamespace, propertyRecord->GetPropertyId(), obj, scriptContext);
- }
- }
- DynamicObject::FromVar(exportsNamespace)->PreventExtensions();
- return exportsNamespace;
- }
- void WebAssemblyInstance::LoadImports(
- WebAssemblyModule * wasmModule,
- ScriptContext* ctx,
- Var ffi,
- WebAssemblyEnvironment* env)
- {
- const uint32 importCount = wasmModule->GetImportCount();
- if (importCount > 0 && (!ffi || !JavascriptOperators::IsObject(ffi)))
- {
- JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidImport);
- }
- uint32 counters[Wasm::ExternalKinds::Limit];
- memset(counters, 0, sizeof(counters));
- for (uint32 i = 0; i < importCount; ++i)
- {
- Wasm::WasmImport* import = wasmModule->GetImport(i);
- Var prop = GetImportVariable(import, ctx, ffi);
- uint32& counter = counters[import->kind];
- switch (import->kind)
- {
- case Wasm::ExternalKinds::Function:
- {
- if (!JavascriptFunction::Is(prop))
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidImport, import->modName, import->importName, _u("Function"));
- }
- Assert(counter < wasmModule->GetImportedFunctionCount());
- Assert(wasmModule->GetFunctionIndexType(counter) == Wasm::FunctionIndexTypes::ImportThunk);
- env->SetImportedFunction(counter, prop);
- if (WasmScriptFunction::Is(prop))
- {
- Assert(env->GetWasmFunction(counter) == nullptr);
- WasmScriptFunction* func = WasmScriptFunction::FromVar(prop);
- if (!wasmModule->GetWasmFunctionInfo(counter)->GetSignature()->IsEquivalent(func->GetSignature()))
- {
- char16 temp[2048] = { 0 };
- char16 importargs[512] = { 0 };
- wasmModule->GetWasmFunctionInfo(counter)->GetSignature()->WriteSignatureToString(importargs, 512);
- char16 exportargs[512] = { 0 };
- func->GetSignature()->WriteSignatureToString(exportargs, 512);
- _snwprintf_s(temp, 2048, _TRUNCATE, _u("%ls%ls to %ls%ls"), func->GetDisplayName()->GetString(), exportargs, import->importName, importargs);
- // this makes a copy of the error message buffer, so it's fine to not worry about clean-up
- JavascriptError::ThrowWebAssemblyLinkError(ctx, WASMERR_LinkSignatureMismatch, temp);
- }
- // Imported Wasm functions can be called directly
- env->SetWasmFunction(counter, func);
- }
- break;
- }
- case Wasm::ExternalKinds::Memory:
- {
- Assert(wasmModule->HasMemoryImport());
- if (wasmModule->HasMemoryImport())
- {
- if (!WebAssemblyMemory::Is(prop))
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidImport, import->modName, import->importName, _u("WebAssembly.Memory"));
- }
- WebAssemblyMemory * mem = WebAssemblyMemory::FromVar(prop);
- if (mem->GetInitialLength() < wasmModule->GetMemoryInitSize())
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidInitialSize, _u("WebAssembly.Memory"), mem->GetInitialLength(), wasmModule->GetMemoryInitSize());
- }
- if (mem->GetMaximumLength() > wasmModule->GetMemoryMaxSize())
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidMaximumSize, _u("WebAssembly.Memory"), mem->GetMaximumLength(), wasmModule->GetMemoryMaxSize());
- }
- env->SetMemory(counter, mem);
- }
- break;
- }
- case Wasm::ExternalKinds::Table:
- {
- Assert(wasmModule->HasTableImport());
- if (wasmModule->HasTableImport())
- {
- if (!WebAssemblyTable::Is(prop))
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidImport, import->modName, import->importName, _u("WebAssembly.Table"));
- }
- WebAssemblyTable * table = WebAssemblyTable::FromVar(prop);
- if (table->GetInitialLength() < wasmModule->GetTableInitSize())
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidInitialSize, _u("WebAssembly.Table"), table->GetInitialLength(), wasmModule->GetTableInitSize());
- }
- if (table->GetMaximumLength() > wasmModule->GetTableMaxSize())
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidMaximumSize, _u("WebAssembly.Table"), table->GetMaximumLength(), wasmModule->GetTableMaxSize());
- }
- env->SetTable(counter, table);
- }
- break;
- }
- case Wasm::ExternalKinds::Global:
- {
- Wasm::WasmGlobal* global = wasmModule->GetGlobal(counter);
- if (global->IsMutable() || (!JavascriptNumber::Is(prop) && !TaggedInt::Is(prop)))
- {
- JavascriptError::ThrowWebAssemblyLinkErrorVar(ctx, WASMERR_InvalidImport, import->modName, import->importName, _u("Number"));
- }
- Assert(global->GetReferenceType() == Wasm::GlobalReferenceTypes::ImportedReference);
- Wasm::WasmConstLitNode cnst = {0};
- switch (global->GetType())
- {
- case Wasm::WasmTypes::I32: cnst.i32 = JavascriptConversion::ToInt32(prop, ctx); break;
- case Wasm::WasmTypes::F32: cnst.f32 = (float)JavascriptConversion::ToNumber(prop, ctx); break;
- case Wasm::WasmTypes::F64: cnst.f64 = JavascriptConversion::ToNumber(prop, ctx); break;
- case Wasm::WasmTypes::I64: Js::JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidTypeConversion);
- default:
- Js::Throw::InternalError();
- }
- env->SetGlobalValue(global, cnst);
- break;
- }
- default:
- Js::Throw::InternalError();
- }
- ++counter;
- }
- }
- void WebAssemblyInstance::InitialGlobals(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env)
- {
- uint count = wasmModule->GetGlobalCount();
- for (uint i = 0; i < count; i++)
- {
- Wasm::WasmGlobal* global = wasmModule->GetGlobal(i);
- Wasm::WasmConstLitNode cnst = {};
- if (global->GetReferenceType() == Wasm::GlobalReferenceTypes::ImportedReference)
- {
- // the value should already be resolved
- continue;
- }
- if (global->GetReferenceType() == Wasm::GlobalReferenceTypes::LocalReference)
- {
- Wasm::WasmGlobal* sourceGlobal = wasmModule->GetGlobal(global->GetGlobalIndexInit());
- if (sourceGlobal->GetReferenceType() != Wasm::GlobalReferenceTypes::Const &&
- sourceGlobal->GetReferenceType() != Wasm::GlobalReferenceTypes::ImportedReference)
- {
- JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidGlobalRef);
- }
- if (sourceGlobal->GetType() != global->GetType())
- {
- JavascriptError::ThrowTypeError(ctx, WASMERR_InvalidTypeConversion);
- }
- cnst = env->GetGlobalValue(sourceGlobal);
- }
- else
- {
- cnst = global->GetConstInit();
- }
- env->SetGlobalValue(global, cnst);
- }
- }
- void WebAssemblyInstance::InitializeFunctionTable(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env)
- {
- WebAssemblyTable* table = env->GetTable(0);
- Assert(table != nullptr);
- for (uint elementsIndex = 0; elementsIndex < wasmModule->GetElementSegCount(); ++elementsIndex)
- {
- Wasm::WasmElementSegment* eSeg = wasmModule->GetElementSeg(elementsIndex);
- if (eSeg->GetNumElements() > 0)
- {
- uint offset = env->GetElementSegmentOffset(elementsIndex);
- for (uint segIndex = 0; segIndex < eSeg->GetNumElements(); ++segIndex)
- {
- uint funcIndex = eSeg->GetElement(segIndex);
- Var funcObj = env->GetWasmFunction(funcIndex);
- table->DirectSetValue(segIndex + offset, funcObj);
- }
- }
- }
- }
- void WebAssemblyInstance::ValidateTableAndMemory(WebAssemblyModule * wasmModule, ScriptContext* ctx, WebAssemblyEnvironment* env)
- {
- WebAssemblyTable* table = env->GetTable(0);
- if (wasmModule->HasTableImport())
- {
- if (table == nullptr)
- {
- JavascriptError::ThrowWebAssemblyLinkError(ctx, WASMERR_NeedTableObject);
- }
- }
- else
- {
- table = wasmModule->CreateTable();
- env->SetTable(0, table);
- }
- WebAssemblyMemory* mem = env->GetMemory(0);
- if (wasmModule->HasMemoryImport())
- {
- if (mem == nullptr)
- {
- JavascriptError::ThrowWebAssemblyLinkError(ctx, WASMERR_NeedMemoryObject);
- }
- }
- else
- {
- mem = wasmModule->CreateMemory();
- if (mem == nullptr)
- {
- JavascriptError::ThrowWebAssemblyLinkError(ctx, WASMERR_MemoryCreateFailed);
- }
- env->SetMemory(0, mem);
- }
- ArrayBuffer * buffer = mem->GetBuffer();
- if (buffer->IsDetached())
- {
- JavascriptError::ThrowTypeError(wasmModule->GetScriptContext(), JSERR_DetachedTypedArray);
- }
- env->CalculateOffsets(table, mem);
- }
- } // namespace Js
- #endif // ENABLE_WASM
|