Skip to content

Commit b7e1989

Browse files
authored
[wasm2js] Support exports of Globals (#4523)
Export an object with a `.value` property like the wasm JS API does in browsers, and implement them with a getter and setter. Fixes #4522
1 parent 86acb00 commit b7e1989

File tree

7 files changed

+169
-10
lines changed

7 files changed

+169
-10
lines changed

src/emscripten-optimizer/parser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ IString ATOMICS("Atomics");
107107
IString COMPARE_EXCHANGE("compareExchange");
108108
IString LOAD("load");
109109
IString STORE("store");
110+
IString GETTER("get");
111+
IString SETTER("set");
110112

111113
IStringSet
112114
keywords("var const function if else do while for break continue return "

src/emscripten-optimizer/parser.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ extern IString ATOMICS;
124124
extern IString COMPARE_EXCHANGE;
125125
extern IString LOAD;
126126
extern IString STORE;
127+
extern IString GETTER;
128+
extern IString SETTER;
127129

128130
extern IStringSet keywords;
129131

@@ -207,8 +209,8 @@ template<class NodeRef, class Builder> class Parser {
207209
};
208210

209211
struct Frag {
210-
// MSVC does not allow unrestricted unions:
211-
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
212+
// MSVC does not allow unrestricted unions:
213+
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
212214
#ifndef _MSC_VER
213215
union {
214216
#endif

src/emscripten-optimizer/simple_ast.h

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,12 +1463,24 @@ struct JSPrinter {
14631463
newline();
14641464
}
14651465
bool needQuote = false;
1466+
const char* getterSetter = nullptr;
1467+
const char* setterParam = nullptr;
14661468
const char* str;
14671469
if (args[i][0]->isArray()) {
1468-
assert(args[i][0][0] == STRING);
1469-
// A quoted string.
1470-
needQuote = true;
1471-
str = args[i][0][1]->getCString();
1470+
if (args[i][0][0] == STRING) {
1471+
// A quoted string.
1472+
needQuote = true;
1473+
str = args[i][0][1]->getCString();
1474+
} else if (args[i][0][0] == GETTER) {
1475+
getterSetter = GETTER.c_str();
1476+
str = args[i][0][1]->getCString();
1477+
} else if (args[i][0][0] == SETTER) {
1478+
getterSetter = SETTER.c_str();
1479+
str = args[i][0][1]->getCString();
1480+
setterParam = args[i][0][2]->getCString();
1481+
} else {
1482+
abort();
1483+
}
14721484
} else {
14731485
// Just a raw string, no quotes.
14741486
str = args[i][0]->getCString();
@@ -1481,14 +1493,26 @@ struct JSPrinter {
14811493
}
14821494
check++;
14831495
}
1496+
if (getterSetter != nullptr) {
1497+
emit(getterSetter);
1498+
space();
1499+
}
14841500
if (needQuote) {
14851501
emit('"');
14861502
}
14871503
emit(str);
14881504
if (needQuote) {
14891505
emit('"');
14901506
}
1491-
emit(":");
1507+
if (getterSetter != nullptr) {
1508+
emit('(');
1509+
if (setterParam != nullptr) {
1510+
emit(setterParam);
1511+
}
1512+
emit(')');
1513+
} else {
1514+
emit(":");
1515+
}
14921516
space();
14931517
print(args[i][1]);
14941518
}
@@ -1828,6 +1852,26 @@ class ValueBuilder {
18281852
&makeRawArray(2)->push_back(makeString(key)).push_back(value));
18291853
}
18301854

1855+
static void appendToObjectAsGetter(Ref array, IString key, Ref value) {
1856+
assert(array[0] == OBJECT);
1857+
array[1]->push_back(&makeRawArray(2)
1858+
->push_back(&makeRawArray(2)
1859+
->push_back(makeRawString(GETTER))
1860+
.push_back(makeRawString(key)))
1861+
.push_back(value));
1862+
}
1863+
1864+
static void
1865+
appendToObjectAsSetter(Ref array, IString key, IString param, Ref value) {
1866+
assert(array[0] == OBJECT);
1867+
array[1]->push_back(&makeRawArray(2)
1868+
->push_back(&makeRawArray(3)
1869+
->push_back(makeRawString(SETTER))
1870+
.push_back(makeRawString(key))
1871+
.push_back(makeRawString(param)))
1872+
.push_back(value));
1873+
}
1874+
18311875
static Ref makeSub(Ref obj, Ref index) {
18321876
return &makeRawArray(2)
18331877
->push_back(makeRawString(SUB))

src/wasm2js.h

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -764,10 +764,40 @@ void Wasm2JSBuilder::addExports(Ref ast, Module* wasm) {
764764
break;
765765
}
766766
case ExternalKind::Global: {
767+
Ref object = ValueBuilder::makeObject();
768+
769+
IString identName = fromName(export_->value, NameScope::Top);
770+
771+
// getter
772+
{
773+
Ref block = ValueBuilder::makeBlock();
774+
775+
block[1]->push_back(
776+
ValueBuilder::makeReturn(ValueBuilder::makeName(identName)));
777+
778+
ValueBuilder::appendToObjectAsGetter(object, IString("value"), block);
779+
}
780+
781+
// setter
782+
{
783+
std::ostringstream buffer;
784+
buffer << '_' << identName.c_str();
785+
auto setterParam = stringToIString(buffer.str());
786+
787+
auto block = ValueBuilder::makeBlock();
788+
789+
block[1]->push_back(
790+
ValueBuilder::makeBinary(ValueBuilder::makeName(identName),
791+
SET,
792+
ValueBuilder::makeName(setterParam)));
793+
794+
ValueBuilder::appendToObjectAsSetter(
795+
object, IString("value"), setterParam, block);
796+
}
797+
767798
ValueBuilder::appendToObjectWithQuotes(
768-
exports,
769-
fromName(export_->name, NameScope::Export),
770-
ValueBuilder::makeName(fromName(export_->value, NameScope::Top)));
799+
exports, fromName(export_->name, NameScope::Export), object);
800+
771801
break;
772802
}
773803
case ExternalKind::Tag:
@@ -2600,6 +2630,7 @@ void Wasm2JSGlue::emitPostES6() {
26002630
for (auto& exp : wasm.exports) {
26012631
switch (exp->kind) {
26022632
case ExternalKind::Function:
2633+
case ExternalKind::Global:
26032634
case ExternalKind::Memory:
26042635
break;
26052636

test/wasm2js/export_global.2asm.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
function asmFunc(env) {
3+
var Math_imul = Math.imul;
4+
var Math_fround = Math.fround;
5+
var Math_abs = Math.abs;
6+
var Math_clz32 = Math.clz32;
7+
var Math_min = Math.min;
8+
var Math_max = Math.max;
9+
var Math_floor = Math.floor;
10+
var Math_ceil = Math.ceil;
11+
var Math_trunc = Math.trunc;
12+
var Math_sqrt = Math.sqrt;
13+
var abort = env.abort;
14+
var nan = NaN;
15+
var infinity = Infinity;
16+
var global0 = 655360;
17+
function $0() {
18+
return 42 | 0;
19+
}
20+
21+
return {
22+
"HELLO": {
23+
get value() {
24+
return global0;
25+
},
26+
set value(_global0) {
27+
global0 = _global0;
28+
}
29+
},
30+
"helloWorld": $0
31+
};
32+
}
33+
34+
var retasmFunc = asmFunc( { abort: function() { throw new Error('abort'); }
35+
});
36+
export var HELLO = retasmFunc.HELLO;
37+
export var helloWorld = retasmFunc.helloWorld;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
function asmFunc(env) {
3+
var Math_imul = Math.imul;
4+
var Math_fround = Math.fround;
5+
var Math_abs = Math.abs;
6+
var Math_clz32 = Math.clz32;
7+
var Math_min = Math.min;
8+
var Math_max = Math.max;
9+
var Math_floor = Math.floor;
10+
var Math_ceil = Math.ceil;
11+
var Math_trunc = Math.trunc;
12+
var Math_sqrt = Math.sqrt;
13+
var abort = env.abort;
14+
var nan = NaN;
15+
var infinity = Infinity;
16+
var global0 = 655360;
17+
function $0() {
18+
return 42;
19+
}
20+
21+
return {
22+
"HELLO": {
23+
get value() {
24+
return global0;
25+
},
26+
set value(_global0) {
27+
global0 = _global0;
28+
}
29+
},
30+
"helloWorld": $0
31+
};
32+
}
33+
34+
var retasmFunc = asmFunc( { abort: function() { throw new Error('abort'); }
35+
});
36+
export var HELLO = retasmFunc.HELLO;
37+
export var helloWorld = retasmFunc.helloWorld;

test/wasm2js/export_global.wast

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(global $global0 i32 (i32.const 655360))
3+
(export "HELLO" (global $global0))
4+
(func (result i32) (i32.const 42))
5+
(export "helloWorld" (func 0))
6+
)

0 commit comments

Comments
 (0)