blob: e63d18bdf25ab8a7df7f466c5e0736a1e350ecd0 [file] [log] [blame]
/*
* Copyright 2010-2018 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let instance;
let heap;
let global_arguments;
function isBrowser() {
return typeof self !== 'undefined';
}
let runtime;
if (isBrowser()) {
runtime = {
print: console.log,
stdout: '',
write: function (message) {
this.stdout += message;
const lastNewlineIndex = this.stdout.lastIndexOf('\n');
if (lastNewlineIndex == -1) return;
this.print(this.stdout.substring(0, lastNewlineIndex));
this.stdout = this.stdout.substring(lastNewlineIndex + 1)
},
flush: function () {
this.print(this.stdout);
},
exit: function (status) {
throw Error("Kotlin process called exit (" + status + ")");
}
};
} else {
runtime = {
write: write,
print: print,
flush: function () {
},
exit: quit
};
}
function print_usage() {
// TODO: any reliable way to obtain the current script name?
runtime.print('Usage: d8 --expose-wasm launcher.js -- <program.wasm> <program arg1> <program arg2> ...')
quit(1); // TODO: this is d8 specific
}
function utf8encode(s) {
return unescape(encodeURIComponent(s));
}
function utf8decode(s) {
return decodeURIComponent(escape(s));
}
function fromString(string, pointer) {
for (let i = 0; i < string.length; i++) {
heap[pointer + i] = string.charCodeAt(i);
}
heap[pointer + string.length] = 0;
}
function toString(pointer) {
let string = '';
for (let i = pointer; heap[i] != 0; i++) {
string += String.fromCharCode(heap[i]);
}
return string;
}
function toUTF16String(pointer, size) {
let string = '';
for (let i = pointer; i < pointer + size; i += 2) {
string += String.fromCharCode(heap[i] + heap[i + 1] * 256);
}
return string;
}
function twoIntsToDouble(upper, lower) {
const buffer = new ArrayBuffer(8);
const ints = new Int32Array(buffer);
const doubles = new Float64Array(buffer);
ints[1] = upper;
ints[0] = lower;
return doubles[0];
}
function doubleToTwoInts(value) {
const buffer = new ArrayBuffer(8);
const ints = new Int32Array(buffer);
const doubles = new Float64Array(buffer);
doubles[0] = value;
return {upper: ints[1], lower: ints[0]}
}
function int32ToHeap(value, pointer) {
heap[pointer] = value & 0xff;
heap[pointer + 1] = (value & 0xff00) >>> 8;
heap[pointer + 2] = (value & 0xff0000) >>> 16;
heap[pointer + 3] = (value & 0xff000000) >>> 24;
}
function doubleToReturnSlot(value) {
const twoInts = doubleToTwoInts(value);
instance.exports.ReturnSlot_setDouble(twoInts.upper, twoInts.lower);
}
let konan_dependencies = {
env: {
abort: function () {
throw new Error("abort()");
},
// TODO: Account for file and size.
fgets: function (str, size, file) {
// TODO: readline can't read lines without a newline.
// Browsers cant read from console at all.
fromString(utf8encode(readline() + '\n'), str);
return str;
},
read: function (file, str, size) {
let string = utf8encode(readline() + '\n');
fromString(string.substring(0, size), str);
return string.length;
},
Konan_notify_memory_grow: function() {
heap = new Uint8Array(instance.exports.memory.buffer);
},
Konan_abort: function (pointer) {
throw new Error("Konan_abort(" + utf8decode(toString(pointer)) + ")");
},
Konan_exit: function (status) {
runtime.exit(status);
},
Konan_js_arg_size: function (index) {
if (index >= global_arguments.length) return -1;
return global_arguments[index].length + 1; // + 1 for trailing zero.
},
Konan_js_fetch_arg: function (index, ptr) {
let arg = utf8encode(global_arguments[index]);
fromString(arg, ptr);
},
Konan_date_now: function (pointer) {
let now = Date.now();
let high = Math.floor(now / 0xffffffff);
let low = Math.floor(now % 0xffffffff);
int32ToHeap(low, pointer);
int32ToHeap(high, pointer + 4);
},
// TODO: Account for fd and size.
write: function (fd, str, size) {
if (fd != 1 && fd != 2) throw ("write(" + fd + ", ...)");
// TODO: There is no writeErr() in d8.
// Approximate it with write() to stdout for now.
runtime.write(utf8decode(toString(str)));
},
fflush: function(file) {
runtime.flush();
}
}
};
function linkJavaScriptLibraries() {
konan.libraries.forEach(function (library) {
for (const property in library) {
konan_dependencies.env[property] = library[property];
}
});
}
function invokeModule(inst, args) {
if (args.length < 1) print_usage();
global_arguments = args;
instance = inst;
heap = new Uint8Array(instance.exports.memory.buffer);
let exit_status = 0;
try {
exit_status = instance.exports.Konan_js_main(args.length, isBrowser() ? 0 : 1);
} catch (e) {
runtime.print("Exception executing entry point: " + e);
runtime.print(e.stack);
exit_status = 1;
}
runtime.flush();
return exit_status;
}
// Instantiate module in Browser.
function instantiateAndRun(arraybuffer, args) {
linkJavaScriptLibraries();
WebAssembly.instantiate(arraybuffer, konan_dependencies)
.then(resultObject => invokeModule(resultObject.instance, args));
}
// Instantiate module in d8 synchronously.
function instantiateAndRunSync(arraybuffer, args) {
const module = new WebAssembly.Module(arraybuffer);
linkJavaScriptLibraries();
const instance = new WebAssembly.Instance(module, konan_dependencies);
return invokeModule(instance, args)
}
// Instantiate module in Browser using streaming instantiation.
function instantiateAndRunStreaming(filename) {
linkJavaScriptLibraries();
WebAssembly.instantiateStreaming(fetch(filename), konan_dependencies)
.then(resultObject => invokeModule(resultObject.instance, [filename]));
}
konan.moduleEntry = function (args) {
if (isBrowser()) {
if (!document.currentScript.hasAttribute("wasm")) {
throw new Error('Could not find the wasm attribute pointing to the WebAssembly binary.');
}
const filename = document.currentScript.getAttribute("wasm");
if (typeof WebAssembly.instantiateStreaming === 'function') {
instantiateAndRunStreaming(filename);
} else {
fetch(filename)
.then(response => response.arrayBuffer())
.then(arraybuffer => instantiateAndRun(arraybuffer, [filename]));
}
} else {
// Invoke from d8.
const arrayBuffer = readbuffer(args[0]);
const exitStatus = instantiateAndRunSync(arrayBuffer, args);
quit(exitStatus);
}
};