Changeset 189822 in webkit


Ignore:
Timestamp:
Sep 15, 2015 1:00:23 PM (9 years ago)
Author:
commit-queue@webkit.org
Message:

Implement calls to JavaScript functions in WebAssembly
https://bugs.webkit.org/show_bug.cgi?id=149093

Patch by Sukolsak Sakshuwong <Sukolsak Sakshuwong> on 2015-09-15
Reviewed by Filip Pizlo.

This patch implements calls to JavaScript functions in WebAssembly.
WebAssembly functions can only call JavaScript functions that are
imported to their module via an object that is passed into
loadWebAssembly(). References to JavaScript functions are resolved at
the module's load time, just like asm.js.

  • jsc.cpp:

(GlobalObject::finishCreation):
(functionLoadWebAssembly):

  • tests/stress/wasm-calls.js:
  • tests/stress/wasm/calls.wasm:
  • wasm/JSWASMModule.cpp:

(JSC::JSWASMModule::visitChildren):

  • wasm/JSWASMModule.h:

(JSC::JSWASMModule::importedFunctions):

  • wasm/WASMFunctionCompiler.h:

(JSC::WASMFunctionCompiler::buildCallImport):

  • wasm/WASMFunctionParser.cpp:

(JSC::WASMFunctionParser::parseExpressionI32):
(JSC::WASMFunctionParser::parseExpressionF64):
(JSC::WASMFunctionParser::parseCallImport):

  • wasm/WASMFunctionParser.h:
  • wasm/WASMFunctionSyntaxChecker.h:

(JSC::WASMFunctionSyntaxChecker::buildCallInternal):
(JSC::WASMFunctionSyntaxChecker::buildCallImport):
(JSC::WASMFunctionSyntaxChecker::updateTempStackHeightForCall):

  • wasm/WASMModuleParser.cpp:

(JSC::WASMModuleParser::WASMModuleParser):
(JSC::WASMModuleParser::parse):
(JSC::WASMModuleParser::parseModule):
(JSC::WASMModuleParser::parseFunctionImportSection):
(JSC::WASMModuleParser::getImportedValue):
(JSC::parseWebAssembly):

  • wasm/WASMModuleParser.h:
Location:
trunk/Source/JavaScriptCore
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r189820 r189822  
     12015-09-15  Sukolsak Sakshuwong  <sukolsak@gmail.com>
     2
     3        Implement calls to JavaScript functions in WebAssembly
     4        https://bugs.webkit.org/show_bug.cgi?id=149093
     5
     6        Reviewed by Filip Pizlo.
     7
     8        This patch implements calls to JavaScript functions in WebAssembly.
     9        WebAssembly functions can only call JavaScript functions that are
     10        imported to their module via an object that is passed into
     11        loadWebAssembly(). References to JavaScript functions are resolved at
     12        the module's load time, just like asm.js.
     13
     14        * jsc.cpp:
     15        (GlobalObject::finishCreation):
     16        (functionLoadWebAssembly):
     17        * tests/stress/wasm-calls.js:
     18        * tests/stress/wasm/calls.wasm:
     19        * wasm/JSWASMModule.cpp:
     20        (JSC::JSWASMModule::visitChildren):
     21        * wasm/JSWASMModule.h:
     22        (JSC::JSWASMModule::importedFunctions):
     23        * wasm/WASMFunctionCompiler.h:
     24        (JSC::WASMFunctionCompiler::buildCallImport):
     25        * wasm/WASMFunctionParser.cpp:
     26        (JSC::WASMFunctionParser::parseExpressionI32):
     27        (JSC::WASMFunctionParser::parseExpressionF64):
     28        (JSC::WASMFunctionParser::parseCallImport):
     29        * wasm/WASMFunctionParser.h:
     30        * wasm/WASMFunctionSyntaxChecker.h:
     31        (JSC::WASMFunctionSyntaxChecker::buildCallInternal):
     32        (JSC::WASMFunctionSyntaxChecker::buildCallImport):
     33        (JSC::WASMFunctionSyntaxChecker::updateTempStackHeightForCall):
     34        * wasm/WASMModuleParser.cpp:
     35        (JSC::WASMModuleParser::WASMModuleParser):
     36        (JSC::WASMModuleParser::parse):
     37        (JSC::WASMModuleParser::parseModule):
     38        (JSC::WASMModuleParser::parseFunctionImportSection):
     39        (JSC::WASMModuleParser::getImportedValue):
     40        (JSC::parseWebAssembly):
     41        * wasm/WASMModuleParser.h:
     42
    1432015-09-15  Csaba Osztrogonác  <ossy@webkit.org>
    244
  • trunk/Source/JavaScriptCore/jsc.cpp

    r189616 r189822  
    680680
    681681#if ENABLE(WEBASSEMBLY)
    682         addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 1);
     682        addFunction(vm, "loadWebAssembly", functionLoadWebAssembly, 2);
    683683#endif
    684684        addFunction(vm, "loadModule", functionLoadModule, 1);
     
    14591459    RefPtr<WebAssemblySourceProvider> sourceProvider = WebAssemblySourceProvider::create(reinterpret_cast<Vector<uint8_t>&>(buffer), fileName);
    14601460    SourceCode source(sourceProvider);
     1461    JSObject* imports = exec->argument(1).getObject();
     1462
    14611463    String errorMessage;
    1462     JSWASMModule* module = parseWebAssembly(exec, source, errorMessage);
     1464    JSWASMModule* module = parseWebAssembly(exec, source, imports, errorMessage);
    14631465    if (!module)
    14641466        return JSValue::encode(exec->vm().throwException(exec, createSyntaxError(exec, errorMessage)));
  • trunk/Source/JavaScriptCore/tests/stress/wasm-calls.js

    r189563 r189822  
    99wasm/calls.wasm is generated by pack-asmjs <https://github.com/WebAssembly/polyfill-prototype-1> from the following script:
    1010
    11 function asmModule(global, env, buffer) {
     11function asmModule(global, imports, buffer) {
    1212    "use asm";
     13
     14    var sum = imports.sum;
     15    var max = imports.max;
    1316
    1417    function fibonacci(x) {
     
    3336    }
    3437
     38    function callSum(x, y) {
     39        x = x | 0;
     40        y = y | 0;
     41        return sum(x, y) | 0;
     42    }
     43
     44    function callMax(x, y) {
     45        x = x | 0;
     46        y = y | 0;
     47        return max(x, y) | 0;
     48    }
     49
    3550    return {
    3651        fibonacci: fibonacci,
    3752        gcd: gcd,
    3853        lcm: lcm,
     54        callSum: callSum,
     55        callMax: callMax,
    3956    };
    4057}
    4158*/
    4259
    43 var module = loadWebAssembly("wasm/calls.wasm");
     60var imports = {
     61    sum: (x, y) => x + y,
     62    max: Math.max,
     63};
     64var module = loadWebAssembly("wasm/calls.wasm", imports);
    4465
    4566shouldBe(module.fibonacci(10), 89);
    4667shouldBe(module.gcd(15, 25), 5);
    4768shouldBe(module.lcm(15, 25), 75);
     69
     70shouldBe(module.callSum(1, 2), 3);
     71shouldBe(module.callMax(1, 2), 2);
  • trunk/Source/JavaScriptCore/wasm/JSWASMModule.cpp

    r188099 r189822  
    4949    for (auto function : thisObject->m_functions)
    5050        visitor.append(&function);
     51    for (auto importedFunction : thisObject->m_importedFunctions)
     52        visitor.append(&importedFunction);
    5153}
    5254
  • trunk/Source/JavaScriptCore/wasm/JSWASMModule.h

    r189584 r189822  
    8888    Vector<unsigned>& functionStackHeights() { return m_functionStackHeights; }
    8989    Vector<GlobalVariable>& globalVariables() { return m_globalVariables; }
     90    Vector<WriteBarrier<JSFunction>>& importedFunctions() { return m_importedFunctions; }
    9091
    9192private:
     
    109110    Vector<unsigned> m_functionStackHeights;
    110111    Vector<GlobalVariable> m_globalVariables;
     112    Vector<WriteBarrier<JSFunction>> m_importedFunctions;
    111113};
    112114
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h

    r189744 r189822  
    655655    }
    656656
     657    int buildCallImport(uint32_t functionImportIndex, int, const WASMSignature& signature, WASMExpressionType returnType)
     658    {
     659        boxArgumentsAndAdjustStackPointer(signature.arguments);
     660
     661        JSFunction* function = m_module->importedFunctions()[functionImportIndex].get();
     662        move(TrustedImmPtr(function), GPRInfo::regT0);
     663
     664        callAndUnboxResult(returnType);
     665        return UNUSED;
     666    }
     667
    657668    void appendExpressionList(int&, int) { }
    658669
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionParser.cpp

    r189744 r189822  
    531531        case WASMOpExpressionI32::CallInternal:
    532532            return parseCallInternalExpressionI32(context);
     533        case WASMOpExpressionI32::CallImport:
     534            return parseCallImport(context, WASMExpressionType::I32);
    533535        case WASMOpExpressionI32::Negate:
    534536        case WASMOpExpressionI32::BitNot:
     
    595597        case WASMOpExpressionI32::StoreWithOffset32:
    596598        case WASMOpExpressionI32::CallIndirect:
    597         case WASMOpExpressionI32::CallImport:
    598599        case WASMOpExpressionI32::Conditional:
    599600        case WASMOpExpressionI32::Comma:
     
    878879        case WASMOpExpressionF64::GetGlobal:
    879880            return parseGetGlobalExpressionF64(context);
     881        case WASMOpExpressionF64::CallImport:
     882            return parseCallImport(context, WASMExpressionType::F64);
    880883        case WASMOpExpressionF64::SetLocal:
    881884        case WASMOpExpressionF64::SetGlobal:
     
    886889        case WASMOpExpressionF64::CallInternal:
    887890        case WASMOpExpressionF64::CallIndirect:
    888         case WASMOpExpressionF64::CallImport:
    889891        case WASMOpExpressionF64::Conditional:
    890892        case WASMOpExpressionF64::Comma:
     
    10071009}
    10081010
     1011template <class Context>
     1012ContextExpression WASMFunctionParser::parseCallImport(Context& context, WASMExpressionType returnType)
     1013{
     1014    uint32_t functionImportSignatureIndex;
     1015    READ_COMPACT_UINT32_OR_FAIL(functionImportSignatureIndex, "Cannot read the function import signature index.");
     1016    FAIL_IF_FALSE(functionImportSignatureIndex < m_module->functionImportSignatures().size(), "The function import signature index is incorrect.");
     1017    const WASMFunctionImportSignature& functionImportSignature = m_module->functionImportSignatures()[functionImportSignatureIndex];
     1018    const WASMSignature& signature = m_module->signatures()[functionImportSignature.signatureIndex];
     1019    FAIL_IF_FALSE(signature.returnType == returnType, "Wrong return type.");
     1020
     1021    ContextExpressionList argumentList = parseCallArguments(context, signature.arguments);
     1022    PROPAGATE_ERROR();
     1023    return context.buildCallImport(functionImportSignature.functionImportIndex, argumentList, signature, returnType);
     1024}
     1025
    10091026} // namespace JSC
    10101027
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionParser.h

    r189744 r189822  
    117117    template <class Context> ContextExpressionList parseCallArguments(Context&, const Vector<WASMType>& arguments);
    118118    template <class Context> ContextExpression parseCallInternal(Context&, WASMExpressionType returnType);
     119    template <class Context> ContextExpression parseCallImport(Context&, WASMExpressionType returnType);
    119120
    120121    JSWASMModule* m_module;
  • trunk/Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h

    r189744 r189822  
    145145    {
    146146        size_t argumentCount = signature.arguments.size();
    147 
    148         // Boxed arguments + this argument + call frame header + padding.
    149         m_tempStackTop += argumentCount + 1 + JSStack::CallFrameHeaderSize + 1;
    150         updateTempStackHeight();
    151         m_tempStackTop -= argumentCount + 1 + JSStack::CallFrameHeaderSize + 1;
    152 
     147        updateTempStackHeightForCall(argumentCount);
     148        m_tempStackTop -= argumentCount;
     149        if (returnType != WASMExpressionType::Void) {
     150            m_tempStackTop++;
     151            updateTempStackHeight();
     152        }
     153        return UNUSED;
     154    }
     155
     156    int buildCallImport(uint32_t, int, const WASMSignature& signature, WASMExpressionType returnType)
     157    {
     158        size_t argumentCount = signature.arguments.size();
     159        updateTempStackHeightForCall(argumentCount);
    153160        m_tempStackTop -= argumentCount;
    154161        if (returnType != WASMExpressionType::Void) {
     
    197204    }
    198205
     206    void updateTempStackHeightForCall(size_t argumentCount)
     207    {
     208        // Boxed arguments + this argument + call frame header + maximum padding.
     209        m_tempStackTop += argumentCount + 1 + JSStack::CallFrameHeaderSize + 1;
     210        updateTempStackHeight();
     211        m_tempStackTop -= argumentCount + 1 + JSStack::CallFrameHeaderSize + 1;
     212    }
     213
    199214    unsigned m_numberOfLocals;
    200215    unsigned m_tempStackTop { 0 };
  • trunk/Source/JavaScriptCore/wasm/WASMModuleParser.cpp

    r189584 r189822  
    5050namespace JSC {
    5151
    52 WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source)
     52WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source, JSObject* imports)
    5353    : m_vm(vm)
    5454    , m_globalObject(vm, globalObject)
    5555    , m_source(source)
     56    , m_imports(vm, imports)
    5657    , m_reader(static_cast<WebAssemblySourceProvider*>(source.provider())->data())
    5758{
    5859}
    5960
    60 JSWASMModule* WASMModuleParser::parse(String& errorMessage)
     61JSWASMModule* WASMModuleParser::parse(ExecState* exec, String& errorMessage)
    6162{
    6263    m_module.set(m_vm, JSWASMModule::create(m_vm, m_globalObject->wasmModuleStructure()));
    63     parseModule();
     64    parseModule(exec);
    6465    if (!m_errorMessage.isNull()) {
    6566        errorMessage = m_errorMessage;
     
    6970}
    7071
    71 void WASMModuleParser::parseModule()
     72void WASMModuleParser::parseModule(ExecState* exec)
    7273{
    7374    uint32_t magicNumber;
     
    8283    parseSignatureSection();
    8384    PROPAGATE_ERROR();
    84     parseFunctionImportSection();
     85    parseFunctionImportSection(exec);
    8586    PROPAGATE_ERROR();
    8687    parseGlobalSection();
     
    144145}
    145146
    146 void WASMModuleParser::parseFunctionImportSection()
     147void WASMModuleParser::parseFunctionImportSection(ExecState* exec)
    147148{
    148149    uint32_t numberOfFunctionImports;
     
    152153    m_module->functionImports().reserveInitialCapacity(numberOfFunctionImports);
    153154    m_module->functionImportSignatures().reserveInitialCapacity(numberOfFunctionImportSignatures);
     155    m_module->importedFunctions().reserveInitialCapacity(numberOfFunctionImports);
    154156
    155157    for (uint32_t functionImportIndex = 0; functionImportIndex < numberOfFunctionImports; ++functionImportIndex) {
     
    169171            m_module->functionImportSignatures().uncheckedAppend(functionImportSignature);
    170172        }
     173
     174        JSValue value;
     175        getImportedValue(exec, functionImport.functionName, value);
     176        PROPAGATE_ERROR();
     177        FAIL_IF_FALSE(value.isFunction(), "\"" + functionImport.functionName + "\" is not a function.");
     178        JSFunction* function = jsCast<JSFunction*>(value.asCell());
     179        m_module->importedFunctions().uncheckedAppend(WriteBarrier<JSFunction>(m_vm, m_module.get(), function));
    171180    }
    172181    FAIL_IF_FALSE(m_module->functionImportSignatures().size() == numberOfFunctionImportSignatures, "The number of function import signatures is incorrect.");
     
    322331}
    323332
    324 JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, String& errorMessage)
    325 {
    326     WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source);
    327     return moduleParser.parse(errorMessage);
     333void WASMModuleParser::getImportedValue(ExecState* exec, const String& importName, JSValue& value)
     334{
     335    FAIL_IF_FALSE(m_imports, "Accessing property of non-object.");
     336    Identifier identifier = Identifier::fromString(&m_vm, importName);
     337    PropertySlot slot(m_imports.get());
     338    if (!m_imports->getPropertySlot(exec, identifier, slot))
     339        FAIL_WITH_MESSAGE("Can't find a property named \"" + importName + '"');
     340    FAIL_IF_FALSE(slot.isValue(), "\"" + importName + "\" is not a data property.");
     341    // We only retrieve data properties. So, this does not cause any user-observable effect.
     342    value = slot.getValue(exec, identifier);
     343}
     344
     345JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, JSObject* imports, String& errorMessage)
     346{
     347    WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source, imports);
     348    return moduleParser.parse(exec, errorMessage);
    328349}
    329350
  • trunk/Source/JavaScriptCore/wasm/WASMModuleParser.h

    r189123 r189822  
    4343class WASMModuleParser {
    4444public:
    45     WASMModuleParser(VM&, JSGlobalObject*, const SourceCode&);
    46     JSWASMModule* parse(String& errorMessage);
     45    WASMModuleParser(VM&, JSGlobalObject*, const SourceCode&, JSObject* imports);
     46    JSWASMModule* parse(ExecState*, String& errorMessage);
    4747
    4848private:
    49     void parseModule();
     49    void parseModule(ExecState*);
    5050    void parseConstantPoolSection();
    5151    void parseSignatureSection();
    52     void parseFunctionImportSection();
     52    void parseFunctionImportSection(ExecState*);
    5353    void parseGlobalSection();
    5454    void parseFunctionDeclarationSection();
     
    5757    void parseFunctionDefinition(size_t functionIndex);
    5858    void parseExportSection();
     59    void getImportedValue(ExecState*, const String& importName, JSValue&);
    5960
    6061    VM& m_vm;
    6162    Strong<JSGlobalObject> m_globalObject;
    6263    const SourceCode& m_source;
     64    Strong<JSObject> m_imports;
    6365    WASMReader m_reader;
    6466    Strong<JSWASMModule> m_module;
     
    6668};
    6769
    68 JS_EXPORT_PRIVATE JSWASMModule* parseWebAssembly(ExecState*, const SourceCode&, String& errorMessage);
     70JS_EXPORT_PRIVATE JSWASMModule* parseWebAssembly(ExecState*, const SourceCode&, JSObject* imports, String& errorMessage);
    6971
    7072} // namespace JSC
Note: See TracChangeset for help on using the changeset viewer.