diff options
author | Daniel Wirtz <dcode@dcode.io> | 2020-09-18 17:08:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-18 17:08:58 +0200 |
commit | 1a928bc3ff4b511e81b3f93db8aea872e88abaaf (patch) | |
tree | c36dc3594abcd32e61f1e0a63d85339c266db788 | |
parent | 2548f04c198594a21e4144261e8cfea5de56308a (diff) | |
download | binaryen-1a928bc3ff4b511e81b3f93db8aea872e88abaaf.tar.gz binaryen-1a928bc3ff4b511e81b3f93db8aea872e88abaaf.tar.bz2 binaryen-1a928bc3ff4b511e81b3f93db8aea872e88abaaf.zip |
Update JS API function wrapper (#3128)
Updates the JS API `Function` wrapper introduced in #3115 with bindings for more C API functions. Also adds additional comments to describe the inner workings of wrappers in more detail.
-rw-r--r-- | src/js/binaryen.js-post.js | 68 | ||||
-rw-r--r-- | test/binaryen.js/expressions.js | 1 | ||||
-rw-r--r-- | test/binaryen.js/functions.js | 41 | ||||
-rw-r--r-- | test/binaryen.js/functions.js.txt | 14 |
4 files changed, 104 insertions, 20 deletions
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index b02216509..87a6bacda 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -3042,8 +3042,11 @@ Module['setAllowInliningFunctionsWithLoops'] = function(value) { // Expression wrappers -// Makes a wrapper class with the specified static members while -// automatically deriving instance methods and accessors. +// Private symbol used to store the underlying C-API pointer of a wrapped object. +const thisPtr = Symbol(); + +// Makes a specific expression wrapper class with the specified static members +// while automatically deriving instance methods and accessors. function makeExpressionWrapper(ownStaticMembers) { function SpecificExpression(expr) { // can call the constructor without `new` @@ -3059,21 +3062,30 @@ function makeExpressionWrapper(ownStaticMembers) { Object.assign(SpecificExpression, ownStaticMembers); // inherit from Expression (SpecificExpression.prototype = Object.create(Expression.prototype)).constructor = SpecificExpression; - // make own instance members - makeWrapperInstanceMembers(SpecificExpression.prototype, ownStaticMembers); + // derive own instance members + deriveWrapperInstanceMembers(SpecificExpression.prototype, ownStaticMembers); return SpecificExpression; } -// Makes instance members from the given static members -function makeWrapperInstanceMembers(prototype, staticMembers, ref = 'expr') { +// Derives the instance members of a wrapper class from the given static +// members. +function deriveWrapperInstanceMembers(prototype, staticMembers) { + // Given a static member `getName(ptr)` for example, an instance method + // `getName()` and a `name` accessor with the `this` argument bound will be + // derived and added to the wrapper's prototype. If a corresponding static + // `setName(ptr)` is present, a setter for the `name` accessor will be added + // as well. Object.keys(staticMembers).forEach(memberName => { const member = staticMembers[memberName]; if (typeof member === "function") { - // Instance method calls the respective static method + // Instance method calls the respective static method with `this` bound. prototype[memberName] = function(...args) { - return this.constructor[memberName](this[ref], ...args); + return this.constructor[memberName](this[thisPtr], ...args); }; - // Instance accessor calls the respective static methods + // Instance accessors call the respective static methods. Accessors are + // derived only if the respective underlying static method takes exactly + // one argument, the `this` argument, e.g. `getChild(ptr, idx)` does not + // trigger an accessor. let match; if (member.length === 1 && (match = memberName.match(/^(get|is)/))) { const index = match[1].length; @@ -3081,10 +3093,10 @@ function makeWrapperInstanceMembers(prototype, staticMembers, ref = 'expr') { const setterIfAny = staticMembers["set" + memberName.substring(index)]; Object.defineProperty(prototype, propertyName, { get() { - return member(this[ref]); + return member(this[thisPtr]); }, set(value) { - if (setterIfAny) setterIfAny(this[ref], value); + if (setterIfAny) setterIfAny(this[thisPtr], value); else throw Error("property '" + propertyName + "' has no setter"); } }); @@ -3096,7 +3108,7 @@ function makeWrapperInstanceMembers(prototype, staticMembers, ref = 'expr') { // Base class of all expression wrappers function Expression(expr) { if (!expr) throw Error("expression reference must not be null"); - this['expr'] = expr; + this[thisPtr] = expr; } Expression['getId'] = function(expr) { return Module['_BinaryenExpressionGetId'](expr); @@ -3113,9 +3125,9 @@ Expression['finalize'] = function(expr) { Expression['toText'] = function(expr) { return Module['emitText'](expr); }; -makeWrapperInstanceMembers(Expression.prototype, Expression); +deriveWrapperInstanceMembers(Expression.prototype, Expression); Expression.prototype['valueOf'] = function() { - return this['expr']; + return this[thisPtr]; }; Module['Expression'] = Expression; @@ -4235,16 +4247,29 @@ Module['TupleExtract'] = makeExpressionWrapper({ // Function wrapper Module['Function'] = (() => { + // Closure compiler doesn't allow multiple `Function`s at top-level, so: function Function(func) { if (!(this instanceof Function)) { if (!func) return null; return new Function(func); } if (!func) throw Error("function reference must not be null"); - this['func'] = func; + this[thisPtr] = func; } Function['getName'] = function(func) { - return Module['_BinaryenFunctionGetName'](func); + return UTF8ToString(Module['_BinaryenFunctionGetName'](func)); + }; + Function['getParams'] = function(func) { + return Module['_BinaryenFunctionGetParams'](func); + }; + Function['getResults'] = function(func) { + return Module['_BinaryenFunctionGetResults'](func); + }; + Function['getNumVars'] = function(func) { + return Module['_BinaryenFunctionGetNumVars'](func); + }; + Function['getVar'] = function(func, index) { + return Module['_BinaryenFunctionGetVar'](func, index); }; Function['getNumLocals'] = function(func) { return Module['_BinaryenFunctionGetNumLocals'](func); @@ -4260,10 +4285,15 @@ Module['Function'] = (() => { Module['_BinaryenFunctionSetLocalName'](func, index, strToStack(name)); }); }; - // TODO: add more methods - makeWrapperInstanceMembers(Function.prototype, Function, 'func'); + Function['getBody'] = function(func) { + return Module['_BinaryenFunctionGetBody'](func); + }; + Function['setBody'] = function(func, bodyExpr) { + Module['_BinaryenFunctionSetBody'](func, bodyExpr); + }; + deriveWrapperInstanceMembers(Function.prototype, Function); Function.prototype['valueOf'] = function() { - return this['func']; + return this[thisPtr]; }; return Function; })(); diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js index a9a0ab982..b9a9a24fb 100644 --- a/test/binaryen.js/expressions.js +++ b/test/binaryen.js/expressions.js @@ -17,7 +17,6 @@ console.log("# Expression"); assert(typeof binaryen.Block.getName === "function"); // own assert(typeof theExpression.getId === "function"); // proto assert(typeof theExpression.getName === "function"); // own - assert(theExpression.expr === 42); assert((theExpression | 0) === 42); // via valueOf })(); diff --git a/test/binaryen.js/functions.js b/test/binaryen.js/functions.js index b50632dfa..84f59350b 100644 --- a/test/binaryen.js/functions.js +++ b/test/binaryen.js/functions.js @@ -32,3 +32,44 @@ module.removeFunction("a-function"); assert(module.validate()); console.log(module.emitText()); + +// Test wrapper + +func = module.addFunction("b-function", + binaryen.createType([binaryen.i32, binaryen.i32]), + binaryen.i32, + [ binaryen.i32, binaryen.f64 ], + module.local.tee(2, + module.i32.add( + module.local.get(0, binaryen.i32), + module.local.get(1, binaryen.i32) + ), + binaryen.i32 + ) +); +binaryen.Function.setLocalName(func, 0, "a"); +binaryen.Function.setLocalName(func, 1, "b"); +binaryen.Function.setLocalName(func, 2, "ret"); +binaryen.Function.setLocalName(func, 3, "unused"); + +var theFunc = binaryen.Function(func); +assert(theFunc.name === "b-function"); +assert(theFunc.params === binaryen.createType([binaryen.i32, binaryen.i32])); +assert(theFunc.results === binaryen.i32); +assert(theFunc.numVars === 2); +assert(theFunc.getVar(0) === binaryen.i32); +assert(theFunc.getVar(1) === binaryen.f64); +assert(theFunc.numLocals === 4); +assert(theFunc.getLocalName(0) === "a"); +assert(theFunc.getLocalName(1) === "b"); +assert(theFunc.getLocalName(2) === "ret"); +assert(theFunc.getLocalName(3) === "unused"); +theFunc.setLocalName(2, "res"); +assert(theFunc.getLocalName(2) === "res"); +assert((theFunc | 0) === func); + +assert(module.validate()); + +console.log(module.emitText()); + +module.dispose(); diff --git a/test/binaryen.js/functions.js.txt b/test/binaryen.js/functions.js.txt index 8dd90b3a8..bf6337e0e 100644 --- a/test/binaryen.js/functions.js.txt +++ b/test/binaryen.js/functions.js.txt @@ -6,3 +6,17 @@ getExpressionInfo(body)={"id":14,"value":3} (module ) +(module + (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) + (func $b-function (param $a i32) (param $b i32) (result i32) + (local $res i32) + (local $unused f64) + (local.tee $res + (i32.add + (local.get $a) + (local.get $b) + ) + ) + ) +) + |