summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wirtz <dcode@dcode.io>2020-09-18 17:08:58 +0200
committerGitHub <noreply@github.com>2020-09-18 17:08:58 +0200
commit1a928bc3ff4b511e81b3f93db8aea872e88abaaf (patch)
treec36dc3594abcd32e61f1e0a63d85339c266db788
parent2548f04c198594a21e4144261e8cfea5de56308a (diff)
downloadbinaryen-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.js68
-rw-r--r--test/binaryen.js/expressions.js1
-rw-r--r--test/binaryen.js/functions.js41
-rw-r--r--test/binaryen.js/functions.js.txt14
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)
+ )
+ )
+ )
+)
+