mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Initial demangler implementation, i.e. for use with TypeScript definitions
This commit is contained in:
parent
25cf51833d
commit
d678807286
39
lib/demangle/README.md
Normal file
39
lib/demangle/README.md
Normal file
@ -0,0 +1,39 @@
|
||||
 demangle
|
||||
======================
|
||||
|
||||
Demangles AssemblyScript module exports to a friendly object structure compatible with WebIDL and TypeScript definitions.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```js
|
||||
var module = require("@assemblyscript/demangle")(myWasmModule.instance.exports);
|
||||
// use module as defined in the .d.ts
|
||||
```
|
||||
|
||||
Converting a memory offset (`this` value) to a class instance, i.e. where a class instance is returned by a WebAssembly function:
|
||||
|
||||
```js
|
||||
|
||||
var thisValue = wasmFunctionReturningAClassInstance();
|
||||
var myClass = MyClass.wrap(thisValue);
|
||||
```
|
||||
|
||||
Converting a class instance to a memory offset (`this` value), i.e. where calling a WebAssembly function that expects a class instance:
|
||||
|
||||
```js
|
||||
var thisValue = myClass.this;
|
||||
wasmFunctionExpectingAClassInstance(thisValue);
|
||||
```
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
AssemblyScript modules expose their exported elements by their internal name, which is a JSDoc-style fully qualified name indicating layers of nesting, and this module is able to recreate the original object structure from those names.
|
||||
|
||||
* A `.` seperates a parent from a static child
|
||||
* A `#` separates a parent from an instance child
|
||||
* The `get:` prefix indicates a getter
|
||||
* The `set:` prefix indicates a setter
|
||||
|
||||
Note that the compiler generates implicit getters and setters for instance fields for convenience. Support for instance members is achieved by generating wrappers that prepend the `this` value (offset of the instance in memory returned by the constructor) as the first argument when calling an instance method, getter or setter.
|
@ -1,18 +1,57 @@
|
||||
/**
|
||||
* @file AssemblyScript demangler.
|
||||
*/
|
||||
|
||||
module.exports = demangle;
|
||||
|
||||
/**
|
||||
* Demangles module exports to a friendly object structure compatible with WebIDL and TypeScript
|
||||
* definitions.
|
||||
*/
|
||||
/** Demangles AssemblyScript module exports to a friendly object structure. */
|
||||
function demangle(exports) {
|
||||
var root = {};
|
||||
for (let i in exports) {
|
||||
if (exports.hasOwnProperty(i)) {
|
||||
// TODO
|
||||
for (let internalName in exports) {
|
||||
if (!exports.hasOwnProperty(internalName)) continue;
|
||||
let elem = exports[internalName];
|
||||
let parts = internalName.split(".");
|
||||
let curr = root;
|
||||
while (parts.length > 1) {
|
||||
let part = parts.shift();
|
||||
if (!curr.hasOwnProperty(part)) curr[part] = {};
|
||||
curr = curr[part];
|
||||
}
|
||||
let name = parts[0];
|
||||
let hash = name.indexOf("#");
|
||||
if (hash >= 0) {
|
||||
let className = name.substring(0, hash);
|
||||
let classElem = curr[className];
|
||||
if (typeof classElem === "undefined" || !classElem.prototype) {
|
||||
let ctor = function(...args) {
|
||||
return ctor.wrap(ctor.prototype.constructor(...args));
|
||||
};
|
||||
ctor.prototype = {};
|
||||
ctor.wrap = function(thisValue) {
|
||||
return Object.create(ctor.prototype, { "this": { value: thisValue, writable: false } });
|
||||
};
|
||||
if (classElem) Object.getOwnPropertyNames(classElem).forEach(name =>
|
||||
Object.defineProperty(ctor, name, Object.getOwnPropertyDescriptor(classElem, name))
|
||||
);
|
||||
curr[className] = ctor;
|
||||
}
|
||||
name = name.substring(hash + 1);
|
||||
curr = curr[className].prototype;
|
||||
if (/^(get|set):/.test(name)) {
|
||||
if (!curr.hasOwnProperty(name = name.substring(4))) {
|
||||
let getter = exports[internalName.replace("set:", "get:")];
|
||||
let setter = exports[internalName.replace("get:", "set:")];
|
||||
Object.defineProperty(curr, name, {
|
||||
get: function() { return getter(this.this); },
|
||||
set: function(value) { setter(this.this, value); }
|
||||
});
|
||||
}
|
||||
} else curr[name] = function(...args) { return elem(this.this, ...args); };
|
||||
} else {
|
||||
if (/^(get|set):/.test(name)) {
|
||||
if (!curr.hasOwnProperty(name = name.substring(4))) {
|
||||
Object.defineProperty(curr, name, {
|
||||
get: exports[internalName.replace("set:", "get:")],
|
||||
set: exports[internalName.replace("get:", "set:")]
|
||||
});
|
||||
}
|
||||
} else curr[name] = elem;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
|
13
lib/demangle/package.json
Normal file
13
lib/demangle/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@assemblyscript/demangle",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node tests"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"package.json",
|
||||
"README.md"
|
||||
]
|
||||
}
|
45
lib/demangle/tests/index.js
Normal file
45
lib/demangle/tests/index.js
Normal file
@ -0,0 +1,45 @@
|
||||
var assert = require("assert");
|
||||
var inspect = require("util").inspect;
|
||||
var demangle = require("..");
|
||||
|
||||
var __this = 8;
|
||||
var __usualDoors = 3;
|
||||
var __doors = -1;
|
||||
|
||||
var exports = demangle({
|
||||
"vroom": function() { console.log("vroom", arguments); },
|
||||
"Car.MAX_DOORS": 5,
|
||||
"Car.get:usualDoors": function() { console.log("Car#get:usualDoors", arguments); return __usualDoors; },
|
||||
"Car.set:usualDoors": function(value) { console.log("Car#set:usualDoors", arguments); __usualDoors = value; },
|
||||
"Car#constructor": function(this_, doors) { console.log("Car#constructor", arguments); __doors = doors; return __this; },
|
||||
"Car#openDoors": function(this_) { console.log("Car#openDoors", arguments); return true; },
|
||||
"Car#get:doors": function(this_) { console.log("Car#get:doors", arguments); return __doors; },
|
||||
"Car#set:doors": function(this_, value) { console.log("Car#set:doors", arguments); __doors = value; }
|
||||
});
|
||||
|
||||
console.log(inspect(exports, true));
|
||||
|
||||
exports.vroom(1, 2, 3);
|
||||
|
||||
var Car = exports.Car;
|
||||
|
||||
assert(Car.usualDoors == 3);
|
||||
exports.Car.usualDoors = exports.Car.usualDoors + 2;
|
||||
assert(Car.usualDoors == 5);
|
||||
|
||||
var car = new exports.Car(2);
|
||||
|
||||
assert(car.this == 8);
|
||||
|
||||
assert(car.openDoors() == true);
|
||||
assert(car.doors == 2);
|
||||
car.doors = car.doors + 2;
|
||||
assert(car.doors == 4);
|
||||
|
||||
console.log(inspect(car, true));
|
||||
|
||||
var wrappedCar = exports.Car.wrap(16);
|
||||
|
||||
assert(wrappedCar.this == 16);
|
||||
|
||||
console.log(inspect(wrappedCar, true));
|
Loading…
x
Reference in New Issue
Block a user