Quellcode durchsuchen

add support for imports

Michael Holman vor 10 Jahren
Ursprung
Commit
5636fcb3eb

+ 13 - 7
bin/ch/WScriptJsrt.cpp

@@ -438,10 +438,11 @@ bool WScriptJsrt::CreateNamedFunction(const wchar_t* nameString, JsNativeFunctio
 JsValueRef __stdcall WScriptJsrt::LoadWasmCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
 {
     HRESULT hr = E_FAIL;
+    JsValueRef ffi = JS_INVALID_REFERENCE;
     JsValueRef returnValue = JS_INVALID_REFERENCE;
     JsErrorCode errorCode = JsNoError;
 
-    if (argumentCount < 2 || argumentCount > 4)
+    if (argumentCount < 2 || argumentCount > 5)
     {
         fwprintf(stderr, L"Too many or too few arguments.\n");
     }
@@ -458,13 +459,18 @@ JsValueRef __stdcall WScriptJsrt::LoadWasmCallback(JsValueRef callee, bool isCon
 
         if (argumentCount > 2)
         {
-            IfJsrtErrorSetGo(ChakraRTInterface::JsBooleanToBool(arguments[2], &isBinaryFormat));
+            ffi = arguments[2];
         }
 
-
         if (argumentCount > 3)
         {
-            IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointer(arguments[3], &scriptInjectType, &scriptInjectTypeLength));
+            IfJsrtErrorSetGo(ChakraRTInterface::JsBooleanToBool(arguments[3], &isBinaryFormat));
+        }
+
+
+        if (argumentCount > 4)
+        {
+            IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointer(arguments[4], &scriptInjectType, &scriptInjectTypeLength));
         }
 
 
@@ -487,7 +493,7 @@ JsValueRef __stdcall WScriptJsrt::LoadWasmCallback(JsValueRef callee, bool isCon
             }
             else
             {
-                returnValue = LoadWasm(fileName, fileNameLength, fileContent, isBinaryFormat, lengthBytes, scriptInjectType);
+                returnValue = LoadWasm(fileName, fileNameLength, fileContent, isBinaryFormat, lengthBytes, scriptInjectType, ffi);
             }
         }
     }
@@ -496,7 +502,7 @@ Error:
     return returnValue;
 }
 
-JsValueRef WScriptJsrt::LoadWasm(LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, const bool isBinary, const UINT lengthBytes, LPCWSTR scriptInjectType)
+JsValueRef WScriptJsrt::LoadWasm(LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, const bool isBinary, const UINT lengthBytes, LPCWSTR scriptInjectType, JsValueRef ffi)
 {
     HRESULT hr = E_FAIL;
     JsErrorCode errorCode = JsNoError;
@@ -519,7 +525,7 @@ JsValueRef WScriptJsrt::LoadWasm(LPCWSTR fileName, size_t fileNameLength, LPCWST
 
     if (wcscmp(scriptInjectType, L"self") == 0)
     {
-        errorCode = ChakraRTInterface::JsRunWasmScript(fileContent, 0, fullPath, isBinary, lengthBytes, &returnValue);
+        errorCode = ChakraRTInterface::JsRunWasmScript(fileContent, 0, fullPath, isBinary, lengthBytes, ffi, &returnValue);
         if (errorCode != JsNoError)
         {
             PrintException(fileName, errorCode);

+ 1 - 1
bin/ch/WScriptJsrt.h

@@ -55,7 +55,7 @@ public:
     static JsValueRef LoadScriptHelper(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState, bool isSourceModule);
 
 #ifdef ENABLE_WASM
-    static JsValueRef LoadWasm(LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, const bool isBinary, const UINT lengthBytes, LPCWSTR scriptInjectType);
+    static JsValueRef LoadWasm(LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, const bool isBinary, const UINT lengthBytes, LPCWSTR scriptInjectType, JsValueRef ffi);
 #endif
 private:
     static bool CreateArgumentsObject(JsValueRef *argsObject);

+ 2 - 2
bin/ch/chakrartinterface.h

@@ -29,7 +29,7 @@ struct JsAPIHooks
     typedef JsErrorCode (WINAPI *JsrtHasPropertyPtr)(JsValueRef object, JsPropertyIdRef property, bool *hasProperty);
     typedef JsErrorCode (WINAPI *JsrtRunScriptPtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result);
     typedef JsErrorCode (WINAPI *JsrtRunModulePtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result);
-    typedef JsErrorCode(WINAPI *JsrtRunWasmScriptPtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, const bool isBinary, const UINT lengthBytes, JsValueRef* result);
+    typedef JsErrorCode(WINAPI *JsrtRunWasmScriptPtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, const bool isBinary, const UINT lengthBytes, JsValueRef ffi, JsValueRef* result);
     typedef JsErrorCode (WINAPI *JsrtCallFunctionPtr)(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result);
     typedef JsErrorCode (WINAPI *JsrtNumberToDoublePtr)(JsValueRef value, double *doubleValue);
     typedef JsErrorCode (WINAPI *JsrtNumberToIntPtr)(JsValueRef value, int *intValue);
@@ -182,7 +182,7 @@ public:
     static JsErrorCode WINAPI JsRunScript(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunScript(script, sourceContext, sourceUrl, result); }
     static JsErrorCode WINAPI JsRunModule(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunModule(script, sourceContext, sourceUrl, result); }
 #ifdef ENABLE_WASM
-    static JsErrorCode WINAPI JsRunWasmScript(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, const bool isBinary, const UINT lengthBytes, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunWasmScript(script, sourceContext, sourceUrl, isBinary, lengthBytes, result); }
+    static JsErrorCode WINAPI JsRunWasmScript(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, const bool isBinary, const UINT lengthBytes, JsValueRef ffi, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunWasmScript(script, sourceContext, sourceUrl, isBinary, lengthBytes, ffi, result); }
 #endif
     static JsErrorCode WINAPI JsCallFunction(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result) { return m_jsApiHooks.pfJsrtCallFunction(function, arguments, argumentCount, result); }
     static JsErrorCode WINAPI JsNumberToDouble(JsValueRef value, double* doubleValue) { return m_jsApiHooks.pfJsrtNumbertoDouble(value, doubleValue); }

+ 1 - 0
lib/Jsrt/ChakraCommon.h

@@ -895,6 +895,7 @@
             _In_z_ const wchar_t *sourceUrl,
             _In_ const bool isBinary,
             _In_ const UINT lengthBytes,
+            _In_opt_ JsValueRef ffi,
             _Out_ JsValueRef *result);
 #endif
 

+ 7 - 2
lib/Jsrt/Jsrt.cpp

@@ -2494,7 +2494,7 @@ STDAPI_(JsErrorCode) JsExperimentalApiRunModule(_In_z_ const wchar_t * script, _
     return RunScriptCore(script, sourceContext, sourceUrl, false, JsParseScriptAttributeNone, true, result);
 }
 
-STDAPI_(JsErrorCode) JsRunWasmScript(_In_z_ const wchar_t * script, _In_ JsSourceContext sourceContext, _In_z_ const wchar_t *sourceUrl, _In_ const bool isBinary, _In_ const uint lengthBytes, _Out_ JsValueRef * result)
+STDAPI_(JsErrorCode) JsRunWasmScript(_In_z_ const wchar_t * script, _In_ JsSourceContext sourceContext, _In_z_ const wchar_t *sourceUrl, _In_ const bool isBinary, _In_ const uint lengthBytes, _In_opt_ JsValueRef ffi, _Out_ JsValueRef * result)
 {
 #ifdef ENABLE_WASM
     Js::JavascriptFunction *scriptFunction;
@@ -2505,6 +2505,11 @@ STDAPI_(JsErrorCode) JsRunWasmScript(_In_z_ const wchar_t * script, _In_ JsSourc
         PARAM_NOT_NULL(script);
         PARAM_NOT_NULL(sourceUrl);
 
+        // FFI is optional
+        if (ffi != JS_INVALID_REFERENCE)
+        {
+            VALIDATE_INCOMING_OBJECT(ffi, scriptContext);
+        }
 
         SourceContextInfo * sourceContextInfo = scriptContext->GetSourceContextInfo(sourceContext, NULL);
 
@@ -2526,7 +2531,7 @@ STDAPI_(JsErrorCode) JsRunWasmScript(_In_z_ const wchar_t * script, _In_ JsSourc
         };
 
         Js::Utf8SourceInfo* utf8SourceInfo;
-        scriptFunction = scriptContext->LoadWasmScript(script, &si, &se, result != NULL, false, false, &utf8SourceInfo, isBinary, lengthBytes, Js::Constants::GlobalCode);
+        scriptFunction = scriptContext->LoadWasmScript(script, &si, &se, result != NULL, false, false, &utf8SourceInfo, isBinary, lengthBytes, Js::Constants::GlobalCode, (Js::Var)ffi);
 
         JsrtContext * context = JsrtContext::GetCurrent();
         context->OnScriptLoad(scriptFunction, utf8SourceInfo);

+ 38 - 10
lib/Runtime/Base/ScriptContext.cpp

@@ -1774,7 +1774,7 @@ namespace Js
     }
 
 #ifdef ENABLE_WASM
-    JavascriptFunction* ScriptContext::LoadWasmScript(const wchar_t* script, SRCINFO const * pSrcInfo, CompileScriptException * pse, bool isExpression, bool disableDeferredParse, bool isForNativeCode, Utf8SourceInfo** ppSourceInfo, const bool isBinary, const uint lengthBytes, const wchar_t *rootDisplayName)
+    JavascriptFunction* ScriptContext::LoadWasmScript(const wchar_t* script, SRCINFO const * pSrcInfo, CompileScriptException * pse, bool isExpression, bool disableDeferredParse, bool isForNativeCode, Utf8SourceInfo** ppSourceInfo, const bool isBinary, const uint lengthBytes, const wchar_t *rootDisplayName, Js::Var ffi)
     {
         if (pSrcInfo == nullptr)
         {
@@ -1864,15 +1864,43 @@ namespace Js
             AsmJsScriptFunction * funcObj = nullptr;
             for (uint i = 0; i < wasmModule->functions->Count(); ++i)
             {
-                funcObj = javascriptLibrary->CreateAsmJsScriptFunction(functionArray[i]->body);
-                funcObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint);
-                funcObj->SetModuleMemory(moduleMemoryPtr);
-                FunctionEntryPointInfo * entypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo();
-                entypointInfo->SetIsAsmJSFunction(true);
-                entypointInfo->address = AsmJsDefaultEntryThunk;
-                entypointInfo->SetModuleAddress((uintptr_t)moduleMemoryPtr);
-                funcObj->SetEnvironment(frameDisplay);
-                localModuleFunctions[i] = funcObj;
+                if (functionArray[i]->wasmInfo->Imported())
+                {
+                    PropertyRecord const * propertyRecord = nullptr;
+                    LPCUTF8 name = functionArray[i]->wasmInfo->GetName();
+
+                    utf8::DecodeOptions decodeOptions = utf8::doAllowInvalidWCHARs;
+
+                    UINT utf16Len = utf8::ByteIndexIntoCharacterIndex(name, strlen((const char*)name), decodeOptions);
+                    LPCWSTR contents = (LPCWSTR)HeapAlloc(GetProcessHeap(), 0, (utf16Len + 1) * sizeof(WCHAR));
+                    if (contents == nullptr)
+                    {
+                        Js::Throw::OutOfMemory();
+                    }
+                    utf8::DecodeIntoAndNullTerminate((wchar_t*)contents, name, utf16Len, decodeOptions);
+
+                    GetOrAddPropertyRecord(contents, utf16Len, &propertyRecord);
+
+                    Var prop = JavascriptOperators::OP_GetProperty(ffi, propertyRecord->GetPropertyId(), this);
+                    if (!JavascriptFunction::Is(prop))
+                    {
+                        Assert(UNREACHED);
+                        // TODO: michhol figure out correct error path
+                    }
+                    localModuleFunctions[i] = prop;
+                }
+                else
+                {
+                    funcObj = javascriptLibrary->CreateAsmJsScriptFunction(functionArray[i]->body);
+                    funcObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint);
+                    funcObj->SetModuleMemory(moduleMemoryPtr);
+                    FunctionEntryPointInfo * entypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo();
+                    entypointInfo->SetIsAsmJSFunction(true);
+                    entypointInfo->address = AsmJsDefaultEntryThunk;
+                    entypointInfo->SetModuleAddress((uintptr_t)moduleMemoryPtr);
+                    funcObj->SetEnvironment(frameDisplay);
+                    localModuleFunctions[i] = funcObj;
+                }
             }
 
             HeapDelete(bytecodeGen);

+ 1 - 1
lib/Runtime/Base/ScriptContext.h

@@ -1096,7 +1096,7 @@ private:
             CompileScriptException * pse, Utf8SourceInfo** ppSourceInfo, const wchar_t *rootDisplayName, LoadScriptFlag loadScriptFlag);
 
 #ifdef ENABLE_WASM
-        JavascriptFunction* LoadWasmScript(const wchar_t* script, SRCINFO const * pSrcInfo, CompileScriptException * pse, bool isExpression, bool disableDeferredParse, bool isForNativeCode, Utf8SourceInfo** ppSourceInfo, const bool isBinary, const uint lengthBytes, const wchar_t *rootDisplayName);
+        JavascriptFunction* LoadWasmScript(const wchar_t* script, SRCINFO const * pSrcInfo, CompileScriptException * pse, bool isExpression, bool disableDeferredParse, bool isForNativeCode, Utf8SourceInfo** ppSourceInfo, const bool isBinary, const uint lengthBytes, const wchar_t *rootDisplayName, Js::Var ffi);
 #endif
 
         ArenaAllocator* GeneralAllocator() { return &generalAllocator; }

+ 27 - 19
lib/WasmReader/SExprParser.cpp

@@ -54,9 +54,11 @@ SExprParser::ReadFromModule()
     switch(tok)
     {
     case wtkFUNC:
-        return ParseFunctionHeader();
+        return ParseFunctionHeader<false>();
     case wtkEXPORT:
         return ParseExport();
+    case wtkIMPORT:
+        return ParseFunctionHeader<true>();
     case wtkMEMORY:
         return ParseMemory();
     // TODO: implement the following
@@ -166,7 +168,7 @@ SExprParser::ReadExprCore(SExprTokenType tok)
     case wtkBLOCK:
         return ParseBlock();
     case wtkCALL:
-        return ParseCall();
+        return ParseCall();;
     case wtkLOOP:
         return wnLOOP;
     case wtkLABEL:
@@ -207,32 +209,38 @@ ParseVarCommon:
     // TODO: implement enumerated ops
     case wtkBREAK:
     case wtkSWITCH:
-    case wtkDISPATCH:
-    case wtkDESTRUCT:
     default:
         ThrowSyntaxError();
     }
 }
 
+template <bool imported>
 WasmOp
 SExprParser::ParseFunctionHeader()
 {
-    m_currentNode.op = wnFUNC;
+    m_currentNode.op = imported ? wnIMPORT : wnFUNC;
 
     SExprTokenType tok = m_scanner->Scan();
 
+    m_funcInfo = Anew(&m_alloc, WasmFunctionInfo, &m_alloc);
+
+    if (imported)
+    {
+        m_funcInfo->SetImported(true);
+    }
+
     if (tok == wtkSTRINGLIT)
     {
-        if (!m_nameToFuncMap->AddNew(m_token.u.m_sz, m_funcNumber))
+        if (imported)
         {
-            ThrowSyntaxError();
+            m_funcInfo->SetName(m_token.u.m_sz);
         }
+        m_nameToFuncMap->AddNew(m_token.u.m_sz, m_funcNumber);
+
         tok = m_scanner->Scan();
     }
 
-    m_funcInfo = Anew(&m_alloc, WasmFunctionInfo, &m_alloc);
     m_currentNode.func.info = m_funcInfo;
-
     m_funcNumber++;
 
     if (IsEndOfExpr(tok))
@@ -272,6 +280,12 @@ SExprParser::ParseFunctionHeader()
         tok = m_scanner->Scan();
     }
 
+    // import should not have locals or a function body
+    if (imported)
+    {
+        ThrowSyntaxError();
+    }
+
     while (tok == wtkLOCAL)
     {
         ParseLocal();
@@ -347,10 +361,7 @@ SExprParser::ParseParam()
     SExprTokenType tok = m_scanner->Scan();
     if (tok == wtkID)
     {
-        if (!m_nameToLocalMap->AddNew(m_token.u.m_sz, m_funcInfo->GetLocalCount()))
-        {
-            ThrowSyntaxError();
-        }
+        m_nameToLocalMap->AddNew(m_token.u.m_sz, m_funcInfo->GetLocalCount());
         tok = m_scanner->Scan();
         m_funcInfo->AddParam(GetWasmType(tok));
         m_scanner->ScanToken(wtkRPAREN);
@@ -379,10 +390,7 @@ SExprParser::ParseLocal()
     SExprTokenType tok = m_scanner->Scan();
     if (tok == wtkID)
     {
-        if (!m_nameToLocalMap->AddNew(m_token.u.m_sz, m_funcInfo->GetLocalCount()))
-        {
-            ThrowSyntaxError();
-        }
+        m_nameToLocalMap->AddNew(m_token.u.m_sz, m_funcInfo->GetLocalCount());
         tok = m_scanner->Scan();
         m_funcInfo->AddLocal(GetWasmType(tok));
         m_scanner->ScanToken(wtkRPAREN);
@@ -464,11 +472,11 @@ WasmOp SExprParser::ParseBlock()
 
 WasmOp SExprParser::ParseCall()
 {
+    m_currentNode.op = wnCALL;
     m_blockNesting->Push(SExpr::Call);
 
     ParseFuncVar();
-
-    return wnCALL;
+    return m_currentNode.op;
 }
 
 WasmOp

+ 1 - 0
lib/WasmReader/SExprParser.h

@@ -44,6 +44,7 @@ namespace Wasm
 
     private:
         WasmOp ReadExprCore(SExprTokenType tok);
+        template <bool imported>
         WasmOp ParseFunctionHeader();
         WasmOp ParseExport();
         WasmOp ParseMemory();

+ 1 - 1
lib/WasmReader/WasmBinaryOpCodes.h

@@ -84,7 +84,7 @@ WASM_MISC_OPCODE(SetLocal,       0x0f,        SETLOCAL,          Limit)
 WASM_MISC_OPCODE(GetGlobal,      0x10,        GETGLOBAL,         Limit)
 WASM_MISC_OPCODE(SetGlobal,      0x11,        SETGLOBAL,         Limit)
 WASM_MISC_OPCODE(Call,           0x12,        CALL,              Limit)
-WASM_MISC_OPCODE(CallIndirect,   0x13,        DISPATCH,          Limit)
+WASM_MISC_OPCODE(CallIndirect,   0x13,        LIMIT,             Limit)
 
 // Load memory expressions.
 // TODO: Map to node ops

+ 67 - 17
lib/WasmReader/WasmByteCodeGenerator.cpp

@@ -64,6 +64,9 @@ WasmBytecodeGenerator::GenerateModule()
             }
             m_module->functions->Add(GenerateFunction());
             break;
+        case wnIMPORT:
+            m_module->functions->Add(InitializeImport());
+            break;
         case wnEXPORT:
             AddExport();
             break;
@@ -82,10 +85,20 @@ WasmBytecodeGenerator::GenerateModule()
     return m_module;
 }
 
+WasmFunction *
+WasmBytecodeGenerator::InitializeImport()
+{
+    m_func = Anew(&m_alloc, WasmFunction);
+    m_func->wasmInfo = m_reader->m_currentNode.func.info;
+    m_func->imported = true;
+    return m_func;
+}
+
 WasmFunction *
 WasmBytecodeGenerator::GenerateFunction()
 {
     m_func = Anew(&m_alloc, WasmFunction);
+    m_func->imported = false;
     m_func->body = Js::FunctionBody::NewFromRecycler(
         m_scriptContext,
         L"func",
@@ -205,7 +218,7 @@ WasmBytecodeGenerator::GenerateFunction()
     info->SetFloatVarCount(ReservedRegisterCount);
     info->SetDoubleVarCount(ReservedRegisterCount);
 
-    info->SetReturnType(GetAsmJsReturnType());
+    info->SetReturnType(GetAsmJsReturnType(m_funcInfo->GetResultType()));
 
     // REVIEW: overflow checks?
     info->SetIntByteOffset(ReservedRegisterCount * sizeof(Js::Var));
@@ -492,13 +505,21 @@ WasmBytecodeGenerator::EmitCall()
     WasmFunction * callee = m_module->functions->GetBuffer()[funcNum];
 
     // emit start call
-    Js::ArgSlot argSize = (Js::ArgSlot)callee->body->GetAsmJsFunctionInfo()->GetArgByteSize();
-    if (argSize != callee->body->GetAsmJsFunctionInfo()->GetArgByteSize())
+    Js::ArgSlot argSize;
+    Js::OpCodeAsmJs startCallOp;
+    if (callee->imported)
     {
-        throw WasmCompilationException(L"Arg size overflows");
+        // TODO: michhol set upper limit on param count to prevent overflow
+        argSize = (Js::ArgSlot)(callee->wasmInfo->GetParamCount() * sizeof(Js::Var));
+        startCallOp = Js::OpCodeAsmJs::StartCall;
+    }
+    else
+    {
+        argSize = callee->body->GetAsmJsFunctionInfo()->GetArgByteSize();
+        startCallOp = Js::OpCodeAsmJs::I_StartCall;
     }
 
-    m_writer.AsmStartCall(Js::OpCodeAsmJs::I_StartCall, argSize + sizeof(void*));
+    m_writer.AsmStartCall(startCallOp, argSize + sizeof(void*));
 
     WasmOp op;
     uint i = 0;
@@ -519,16 +540,26 @@ WasmBytecodeGenerator::EmitCall()
         switch (info.type)
         {
         case WasmTypes::F32:
+            // REVIEW: support FFI call with f32 params?
+            Assert(!callee->imported);
             argOp = Js::OpCodeAsmJs::I_ArgOut_Flt;
             ++nextLoc;
             break;
         case WasmTypes::F64:
-            argOp = Js::OpCodeAsmJs::I_ArgOut_Db;
-            // this indexes into physical stack, so on x86 we need double width
-            nextLoc += sizeof(double) / sizeof(Js::Var);
+            if (callee->imported)
+            {
+                argOp = Js::OpCodeAsmJs::ArgOut_Db;
+                ++nextLoc;
+            }
+            else
+            {
+                argOp = Js::OpCodeAsmJs::I_ArgOut_Db;
+                // this indexes into physical stack, so on x86 we need double width
+                nextLoc += sizeof(double) / sizeof(Js::Var);
+            }
             break;
         case WasmTypes::I32:
-            argOp = Js::OpCodeAsmJs::I_ArgOut_Int;
+            argOp = callee->imported ? Js::OpCodeAsmJs::ArgOut_Int : Js::OpCodeAsmJs::I_ArgOut_Int;
             ++nextLoc;
             break;
         default:
@@ -563,28 +594,46 @@ WasmBytecodeGenerator::EmitCall()
     // emit call
 
     m_writer.AsmSlot(Js::OpCodeAsmJs::LdSlot, 0, 1, funcNum + m_module->funcOffset);
+
     // calculate number of RegSlots the arguments consume
-    Js::ArgSlot args = (Js::ArgSlot)(::ceil((double)(argSize / sizeof(Js::Var)))) + 1;
-    m_writer.AsmCall(Js::OpCodeAsmJs::I_Call, 0, 0, args, callee->body->GetAsmJsFunctionInfo()->GetReturnType());
+    Js::ArgSlot args;
+    if (callee->imported)
+    {
+        args = (Js::ArgSlot)(callee->wasmInfo->GetParamCount() + 1);
+    }
+    else
+    {
+        args = (Js::ArgSlot)(::ceil((double)(argSize / sizeof(Js::Var)))) + 1;
+    }
+    Js::OpCodeAsmJs callOp = callee->imported ? Js::OpCodeAsmJs::Call : Js::OpCodeAsmJs::I_Call;
+    m_writer.AsmCall(callOp, 0, 0, args, GetAsmJsReturnType(callee->wasmInfo->GetResultType()));
 
     // emit result coercion
     EmitInfo retInfo;
     retInfo.type = callee->wasmInfo->GetResultType();
+    Js::OpCodeAsmJs convertOp = Js::OpCodeAsmJs::Nop;
     switch (retInfo.type)
     {
     case WasmTypes::F32:
+        Assert(!callee->imported);
         retInfo.location = m_f32RegSlots->AcquireTmpRegister();
-        m_writer.AsmReg2(Js::OpCodeAsmJs::I_Conv_VTF, retInfo.location, 0);
+        convertOp = Js::OpCodeAsmJs::I_Conv_VTF;
         break;
     case WasmTypes::F64:
         retInfo.location = m_f64RegSlots->AcquireTmpRegister();
-        m_writer.AsmReg2(Js::OpCodeAsmJs::I_Conv_VTD, retInfo.location, 0);
+        convertOp = callee->imported ? Js::OpCodeAsmJs::Conv_VTF : Js::OpCodeAsmJs::I_Conv_VTF;
         break;
     case WasmTypes::I32:
         retInfo.location = m_i32RegSlots->AcquireTmpRegister();
-        m_writer.AsmReg2(Js::OpCodeAsmJs::I_Conv_VTI, retInfo.location, 0);
+        convertOp = callee->imported ? Js::OpCodeAsmJs::Conv_VTI : Js::OpCodeAsmJs::I_Conv_VTI;
+        break;
+    case WasmTypes::I64:
+        Assert(UNREACHED);
         break;
+    default:
+        Assume(UNREACHED);
     }
+    m_writer.AsmReg2(convertOp, retInfo.location, 0);
 
 
     // track stack requirements for out params
@@ -978,12 +1027,12 @@ WasmBytecodeGenerator::EmitBreak()
     return EmitInfo();
 }
 
-
+/* static */
 Js::AsmJsRetType
-WasmBytecodeGenerator::GetAsmJsReturnType() const
+WasmBytecodeGenerator::GetAsmJsReturnType(WasmTypes::WasmType wasmType)
 {
     Js::AsmJsRetType asmType = Js::AsmJsRetType::Void;
-    switch (m_funcInfo->GetResultType())
+    switch (wasmType)
     {
     case WasmTypes::F32:
         asmType = Js::AsmJsRetType::Float;
@@ -1003,6 +1052,7 @@ WasmBytecodeGenerator::GetAsmJsReturnType() const
     return asmType;
 }
 
+/* static */
 Js::AsmJsVarType
 WasmBytecodeGenerator::GetAsmJsVarType(WasmTypes::WasmType wasmType)
 {

+ 6 - 1
lib/WasmReader/WasmByteCodeGenerator.h

@@ -54,6 +54,7 @@ namespace Wasm
         }
         Js::FunctionBody * body;
         WasmFunctionInfo * wasmInfo;
+        bool imported;
     };
 
     typedef JsUtil::GrowingArray<WasmFunction*, ArenaAllocator> WasmFunctionArray;
@@ -93,9 +94,13 @@ namespace Wasm
         WasmFunction * GenerateFunction();
 
     private:
+
+        WasmFunction * InitializeImport();
+
         EmitInfo EmitExpr(WasmOp op);
         EmitInfo EmitBlock();
         EmitInfo EmitLoop();
+
         EmitInfo EmitCall();
         EmitInfo EmitIfExpr();
         EmitInfo EmitIfElseExpr();
@@ -130,7 +135,7 @@ namespace Wasm
         template <typename T>
         Js::RegSlot GetConstReg(T constVal);
 
-        Js::AsmJsRetType GetAsmJsReturnType() const;
+        static Js::AsmJsRetType GetAsmJsReturnType(WasmTypes::WasmType wasmType);
         static Js::AsmJsVarType GetAsmJsVarType(WasmTypes::WasmType wasmType);
         static Js::ArrayBufferView::ViewType GetViewType(WasmOp op);
         WasmRegisterSpace * GetRegisterSpace(WasmTypes::WasmType type) const;

+ 68 - 81
lib/WasmReader/WasmKeywordSwitch.h

@@ -93,6 +93,58 @@
             break;
         }
         goto LError;
+    case 'c':
+        switch(p[0])
+        {
+        case 'a':
+            switch(p[1])
+            {
+            case 'l':
+                if (p[2] == 'l')
+                {
+                p += 3;
+                token = wtkCALL;
+                goto LKeyword;
+                }
+                break;
+            case 's':
+                if (p[2] == 't')
+                {
+                p += 3;
+                token = wtkCAST;
+                goto LKeyword;
+                }
+                break;
+            }
+            break;
+        case 'o':
+            if (p[1] == 'n' && p[2] == 'v' && p[3] == 'e' && p[4] == 'r')
+            {
+                switch(p[5])
+                {
+                case 't':
+                    switch(p[6])
+                    {
+                    case 's':
+                        p += 7;
+                        token = wtkCONVERTS;
+                        goto LKeyword;
+                        break;
+                    case 'u':
+                        p += 7;
+                        token = wtkCONVERTU;
+                        goto LKeyword;
+                        break;
+                    }
+                    p += 6;
+                    token = wtkCONVERT;
+                    goto LKeyword;
+                    break;
+                }
+            }
+            break;
+        }
+        goto LError;
     case 'n':
         if (p[0] == 'o' && p[1] == 'p')
         {
@@ -141,6 +193,14 @@
             token = wtkIF;
             goto LKeyword;
             break;
+        case 'm':
+            if (p[1] == 'p' && p[2] == 'o' && p[3] == 'r' && p[4] == 't')
+            {
+            p += 5;
+            token = wtkIMPORT;
+            goto LKeyword;
+            }
+            break;
         case '8':
             p += 1;
             token = wtkI8;
@@ -544,87 +604,6 @@
             break;
         }
         goto LError;
-    case 'c':
-        switch(p[0])
-        {
-        case 'a':
-            switch(p[1])
-            {
-            case 'l':
-                if (p[2] == 'l')
-                {
-                p += 3;
-                token = wtkCALL;
-                goto LKeyword;
-                }
-                break;
-            case 's':
-                if (p[2] == 't')
-                {
-                p += 3;
-                token = wtkCAST;
-                goto LKeyword;
-                }
-                break;
-            }
-            break;
-        case 'o':
-            if (p[1] == 'n' && p[2] == 'v' && p[3] == 'e' && p[4] == 'r')
-            {
-                switch(p[5])
-                {
-                case 't':
-                    switch(p[6])
-                    {
-                    case 's':
-                        p += 7;
-                        token = wtkCONVERTS;
-                        goto LKeyword;
-                        break;
-                    case 'u':
-                        p += 7;
-                        token = wtkCONVERTU;
-                        goto LKeyword;
-                        break;
-                    }
-                    p += 6;
-                    token = wtkCONVERT;
-                    goto LKeyword;
-                    break;
-                }
-            }
-            break;
-        }
-        goto LError;
-    case 'd':
-        switch(p[0])
-        {
-        case 'i':
-            if (p[1] == 's' && p[2] == 'p' && p[3] == 'a' && p[4] == 't' && p[5] == 'c' && p[6] == 'h')
-            {
-            p += 7;
-            token = wtkDISPATCH;
-            goto LKeyword;
-            }
-            break;
-        case 'e':
-            if (p[1] == 's' && p[2] == 't' && p[3] == 'r' && p[4] == 'u' && p[5] == 'c' && p[6] == 't')
-            {
-            p += 7;
-            token = wtkDESTRUCT;
-            goto LKeyword;
-            }
-            break;
-        case 'a':
-            if (p[1] == 't' && p[2] == 'a')
-            {
-            p += 3;
-            token = wtkDATA;
-            goto LKeyword;
-            }
-            break;
-        }
-        goto LError;
     case 'r':
         switch(p[0])
         {
@@ -1142,4 +1121,12 @@
         goto LKeyword;
         }
         goto LError;
+    case 'd':
+        if (p[0] == 'a' && p[1] == 't' && p[2] == 'a')
+        {
+        p += 3;
+        token = wtkDATA;
+        goto LKeyword;
+        }
+        goto LError;
 

+ 3 - 3
lib/WasmReader/WasmKeywords.h

@@ -166,6 +166,8 @@ WASM_LOCALTYPE(I64,    i64)
 WASM_LOCALTYPE(F32,    f32)
 WASM_LOCALTYPE(F64,    f64)
 
+WASM_KEYWORD(CALL, call)
+
 // control flow ops
 WASM_KEYWORD(NOP,        nop)
 WASM_KEYWORD(BLOCK,      block)
@@ -175,10 +177,7 @@ WASM_KEYWORD(LOOP,       loop)
 WASM_KEYWORD(LABEL,      label)
 WASM_KEYWORD(BREAK,      break)
 WASM_KEYWORD(SWITCH,     switch)
-WASM_KEYWORD(CALL,       call)
-WASM_KEYWORD(DISPATCH,   dispatch)
 WASM_KEYWORD(RETURN,     return)
-WASM_KEYWORD(DESTRUCT,   destruct)
 WASM_KEYWORD_FDI(CONST,      const)
 
 // structures
@@ -189,6 +188,7 @@ WASM_KEYWORD(LOCAL,      local)
 WASM_KEYWORD(MODULE,     module)
 WASM_KEYWORD(GLOBAL,     global)
 WASM_KEYWORD(EXPORT,     export)
+WASM_KEYWORD(IMPORT,     import)
 WASM_KEYWORD(TABLE,      table)
 WASM_KEYWORD(MEMORY,     memory)
 WASM_KEYWORD(DATA,       data)

+ 1 - 1
test/wasm/basic.js

@@ -3,5 +3,5 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-var a = WScript.LoadWasmFile('basic.wast');
+var a = WScript.LoadWasmFile('basic.wast', {foo: function(a){print(a); return 2;}});
 print(a(11));

+ 2 - 0
test/wasm/basic.wast

@@ -7,6 +7,7 @@
 (func (result i32)
     (return (i32.const 2))
 )
+  (import "foo" (param i32) (result i32))
 (func (param i32) (result i32) (local f32)
     (if (i32.ges (i32.const 26) (i32.const 25)) (setlocal 0 (i32.add (getlocal 0) (i32.const 4))))
 
@@ -24,6 +25,7 @@
     (setlocal 1 (f32.convert_s/i32 (getlocal 0)))
     (setlocal 1 (f32.add (getlocal 1) (getlocal 1)))
     (setlocal 0 (i32.trunc_s/f32 (getlocal 1)))
+    (setlocal 0 (i32.add (getlocal 0) (call 1 (getlocal 0))))
     (i32.store 0 10000 (getlocal 0) (i32.add (getlocal 0) (i32.const 7)))
     (setlocal 0 (i32.load 0 10000 (getlocal 0)))
     (return (i32.add (getlocal 0) (i32.const 42)))

+ 1 - 1
test/wasm/basicBinary.js

@@ -3,5 +3,5 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-var a = WScript.LoadWasmFile('basic.wasm', true);
+var a = WScript.LoadWasmFile('basic.wasm', {}, true);
 print(a(11));

+ 1 - 1
test/wasm/testBinary4.js

@@ -3,5 +3,5 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
-var a = WScript.LoadWasmFile('test4.wasm', true);
+var a = WScript.LoadWasmFile('test4.wasm', {}, true);
 print(a(5));