mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-11 05:51:38 +00:00
Initial demangler implementation, i.e. for use with TypeScript definitions
This commit is contained in:
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;
|
module.exports = demangle;
|
||||||
|
|
||||||
/**
|
/** Demangles AssemblyScript module exports to a friendly object structure. */
|
||||||
* Demangles module exports to a friendly object structure compatible with WebIDL and TypeScript
|
|
||||||
* definitions.
|
|
||||||
*/
|
|
||||||
function demangle(exports) {
|
function demangle(exports) {
|
||||||
var root = {};
|
var root = {};
|
||||||
for (let i in exports) {
|
for (let internalName in exports) {
|
||||||
if (exports.hasOwnProperty(i)) {
|
if (!exports.hasOwnProperty(internalName)) continue;
|
||||||
// TODO
|
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;
|
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));
|
Reference in New Issue
Block a user