aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js5
-rw-r--r--src/determinstic.js8
-rw-r--r--src/embind/embind.js99
-rw-r--r--src/embind/emval.js177
-rw-r--r--src/intertyper.js15
-rw-r--r--src/jsifier.js16
-rw-r--r--src/library.js116
-rw-r--r--src/library_browser.js41
-rw-r--r--src/library_gc.js2
-rw-r--r--src/library_glut.js10
-rw-r--r--src/library_sdl.js12
-rw-r--r--src/parseTools.js50
-rw-r--r--src/postamble.js2
-rw-r--r--src/preamble.js13
-rw-r--r--src/settings.js3
-rw-r--r--src/shell.js1
16 files changed, 414 insertions, 156 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index a131406c..2cc46ab6 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -411,8 +411,9 @@ function analyzer(data, sidePass) {
// legalize parameters
legalizeFunctionParameters(value.params);
// legalize return value, if any
- if (value.assignTo && isIllegalType(item.type)) {
- bits = getBits(value.type);
+ var returnType = getReturnType(item.type);
+ if (value.assignTo && isIllegalType(returnType)) {
+ bits = getBits(returnType);
var elements = getLegalVars(item.assignTo, bits);
// legalize return value
value.assignTo = elements[0].ident;
diff --git a/src/determinstic.js b/src/determinstic.js
index 91f98ed9..1ec0bbfe 100644
--- a/src/determinstic.js
+++ b/src/determinstic.js
@@ -10,3 +10,11 @@ Date.now = function() {
};
performance.now = Date.now;
+function hashMemory(id) {
+ var ret = 0;
+ for (var i = 0; i < HEAPU8.length; i++) {
+ ret = (ret*17 + HEAPU8[i])|0;
+ }
+ print(id + ':' + ret);
+}
+
diff --git a/src/embind/embind.js b/src/embind/embind.js
index cadee700..91386c69 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -131,6 +131,7 @@ function extendError(baseErrorType, errorName) {
// from https://github.com/imvu/imvujs/blob/master/src/function.js
function createNamedFunction(name, body) {
+ name = makeLegalFunctionName(name);
/*jshint evil:true*/
return new Function(
"body",
@@ -270,6 +271,10 @@ function __embind_register_void(rawType, name) {
'fromWireType': function() {
return undefined;
},
+ 'toWireType': function(destructors, o) {
+ // TODO: assert if anything else is given?
+ return undefined;
+ },
});
}
@@ -306,7 +311,7 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following two if()s and assume value is of proper type.
- if (typeof value !== "number") {
+ if (typeof value !== "number" && typeof value !== "boolean") {
throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name);
}
if (value < minRange || value > maxRange) {
@@ -328,8 +333,8 @@ function __embind_register_float(rawType, name) {
'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following if() and assume value is of proper type.
- if (typeof value !== "number") {
- throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name);
+ if (typeof value !== "number" && typeof value !== "boolean") {
+ throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name);
}
return value;
},
@@ -449,6 +454,31 @@ function __embind_register_emval(rawType, name) {
});
}
+function __embind_register_memory_view(rawType, name) {
+ var typeMapping = [
+ Int8Array,
+ Uint8Array,
+ Int16Array,
+ Uint16Array,
+ Int32Array,
+ Uint32Array,
+ Float32Array,
+ Float64Array,
+ ];
+
+ name = readLatin1String(name);
+ registerType(rawType, {
+ name: name,
+ 'fromWireType': function(handle) {
+ var type = HEAPU32[handle >> 2];
+ var size = HEAPU32[(handle >> 2) + 1]; // in elements
+ var data = HEAPU32[(handle >> 2) + 2]; // byte offset into emscripten heap
+ var TA = typeMapping[type];
+ return new TA(HEAP8.buffer, data, size);
+ },
+ });
+}
+
function runDestructors(destructors) {
while (destructors.length) {
var ptr = destructors.pop();
@@ -677,7 +707,7 @@ function __embind_finalize_tuple(rawTupleType) {
},
'toWireType': function(destructors, o) {
if (elementsLength !== o.length) {
- throw new TypeError("Incorrect number of tuple elements");
+ throw new TypeError("Incorrect number of tuple elements for " + reg.name + ": expected=" + elementsLength + ", actual=" + o.length);
}
var ptr = rawConstructor();
for (var i = 0; i < elementsLength; ++i) {
@@ -685,7 +715,7 @@ function __embind_finalize_tuple(rawTupleType) {
}
if (destructors !== null) {
destructors.push(rawDestructor, ptr);
- }
+ }
return ptr;
},
destructorFunction: rawDestructor,
@@ -802,7 +832,9 @@ var genericPointerToWireType = function(destructors, handle) {
if (this.isSmartPointer) {
var ptr = this.rawConstructor();
- destructors.push(this.rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(this.rawDestructor, ptr);
+ }
return ptr;
} else {
return 0;
@@ -854,7 +886,9 @@ var genericPointerToWireType = function(destructors, handle) {
clonedHandle.delete();
})
);
- destructors.push(this.rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(this.rawDestructor, ptr);
+ }
}
break;
@@ -1080,9 +1114,13 @@ ClassHandle.prototype.isAliasOf = function(other) {
return leftClass === rightClass && left === right;
};
+function throwInstanceAlreadyDeleted(obj) {
+ throwBindingError(getInstanceTypeName(obj) + ' instance already deleted');
+}
+
ClassHandle.prototype.clone = function() {
if (!this.$$.ptr) {
- throwBindingError(getInstanceTypeName(this) + ' instance already deleted');
+ throwInstanceAlreadyDeleted(this);
}
var clone = Object.create(Object.getPrototypeOf(this), {
@@ -1104,9 +1142,12 @@ function runDestructor(handle) {
}
}
-ClassHandle.prototype['delete'] = function() {
+ClassHandle.prototype['delete'] = function ClassHandle_delete() {
if (!this.$$.ptr) {
- throwBindingError(getInstanceTypeName(this) + ' instance already deleted');
+ throwInstanceAlreadyDeleted(this);
+ }
+ if (this.$$.deleteScheduled) {
+ throwBindingError('Object already scheduled for deletion');
}
this.$$.count.value -= 1;
@@ -1116,6 +1157,44 @@ ClassHandle.prototype['delete'] = function() {
this.$$.smartPtr = undefined;
this.$$.ptr = undefined;
};
+
+var deletionQueue = [];
+
+ClassHandle.prototype['isDeleted'] = function isDeleted() {
+ return !this.$$.ptr;
+};
+
+ClassHandle.prototype['deleteLater'] = function deleteLater() {
+ if (!this.$$.ptr) {
+ throwInstanceAlreadyDeleted(this);
+ }
+ if (this.$$.deleteScheduled) {
+ throwBindingError('Object already scheduled for deletion');
+ }
+ deletionQueue.push(this);
+ if (deletionQueue.length === 1 && delayFunction) {
+ delayFunction(flushPendingDeletes);
+ }
+ this.$$.deleteScheduled = true;
+ return this;
+};
+
+function flushPendingDeletes() {
+ while (deletionQueue.length) {
+ var obj = deletionQueue.pop();
+ obj.$$.deleteScheduled = false;
+ obj['delete']();
+ }
+}
+Module['flushPendingDeletes'] = flushPendingDeletes;
+
+var delayFunction;
+Module['setDelayFunction'] = function setDelayFunction(fn) {
+ delayFunction = fn;
+ if (deletionQueue.length && delayFunction) {
+ delayFunction(flushPendingDeletes);
+ }
+};
function RegisteredClass(
name,
diff --git a/src/embind/emval.js b/src/embind/emval.js
index c02ffa92..77270597 100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -1,8 +1,12 @@
-/*global Module*/
+/*global Module:true, Runtime*/
/*global HEAP32*/
+/*global new_*/
+/*global createNamedFunction*/
/*global readLatin1String, writeStringToMemory*/
/*global requireRegisteredType, throwBindingError*/
+var Module = Module || {};
+
var _emval_handle_array = [{}]; // reserve zero
var _emval_free_list = [];
@@ -69,14 +73,8 @@ function __emval_incref(handle) {
function __emval_decref(handle) {
if (handle && 0 === --_emval_handle_array[handle].refcount) {
- delete _emval_handle_array[handle];
+ _emval_handle_array[handle] = undefined;
_emval_free_list.push(handle);
-
- var actual_length = _emval_handle_array.length;
- while (actual_length > 0 && _emval_handle_array[actual_length - 1] === undefined) {
- --actual_length;
- }
- _emval_handle_array.length = actual_length;
}
}
@@ -108,44 +106,73 @@ function __emval_take_value(type, v) {
var __newers = {}; // arity -> function
-function __emval_new(handle, argCount, argTypes) {
- requireHandle(handle);
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 3));
+function craftEmvalAllocator(argCount) {
+ /*This function returns a new function that looks like this:
+ function emval_allocator_3(handle, argTypes, arg0Wired, arg1Wired, arg2Wired) {
+ var argType0 = requireRegisteredType(HEAP32[(argTypes >> 2)], "parameter 0");
+ var arg0 = argType0.fromWireType(arg0Wired);
+ var argType1 = requireRegisteredType(HEAP32[(argTypes >> 2) + 1], "parameter 1");
+ var arg1 = argType1.fromWireType(arg1Wired);
+ var argType2 = requireRegisteredType(HEAP32[(argTypes >> 2) + 2], "parameter 2");
+ var arg2 = argType2.fromWireType(arg2Wired);
+ var constructor = _emval_handle_array[handle].value;
+ var emval = new constructor(arg0, arg1, arg2);
+ return emval;
+ } */
+
+ var args1 = ["requireRegisteredType", "HEAP32", "_emval_handle_array", "__emval_register"];
+ var args2 = [requireRegisteredType, HEAP32, _emval_handle_array, __emval_register];
+
+ var argsList = "";
+ var argsListWired = "";
+ for(var i = 0; i < argCount; ++i) {
+ argsList += (i!==0?", ":"")+"arg"+i; // 'arg0, arg1, ..., argn'
+ argsListWired += ", arg"+i+"Wired"; // ', arg0Wired, arg1Wired, ..., argnWired'
+ }
- // Alas, we are forced to use operator new until WebKit enables
- // constructing typed arrays without new.
- // In WebKit, Uint8Array(10) throws an error.
- // In every other browser, it's identical to new Uint8Array(10).
+ var invokerFnBody =
+ "return function emval_allocator_"+argCount+"(handle, argTypes " + argsListWired + ") {\n";
+ for(var i = 0; i < argCount; ++i) {
+ invokerFnBody +=
+ "var argType"+i+" = requireRegisteredType(HEAP32[(argTypes >> 2) + "+i+"], \"parameter "+i+"\");\n" +
+ "var arg"+i+" = argType"+i+".fromWireType(arg"+i+"Wired);\n";
+ }
+ invokerFnBody +=
+ "var constructor = _emval_handle_array[handle].value;\n" +
+ "var obj = new constructor("+argsList+");\n" +
+ "return __emval_register(obj);\n" +
+ "}\n";
+
+ args1.push(invokerFnBody);
+ var invokerFunction = new_(Function, args1).apply(null, args2);
+ return invokerFunction;
+}
+
+function __emval_new(handle, argCount, argTypes) {
+ requireHandle(handle);
+
var newer = __newers[argCount];
if (!newer) {
- var parameters = new Array(argCount);
- for (var i = 0; i < argCount; ++i) {
- parameters[i] = 'a' + i;
- }
- /*jshint evil:true*/
- newer = __newers[argCount] = new Function(
- ['c'].concat(parameters),
- "return new c(" + parameters.join(',') + ");");
+ newer = craftEmvalAllocator(argCount);
+ __newers[argCount] = newer;
}
-
- var constructor = _emval_handle_array[handle].value;
- var obj = newer.apply(undefined, [constructor].concat(args));
-/*
- // implement what amounts to operator new
- function dummy(){}
- dummy.prototype = constructor.prototype;
- var obj = new constructor;
- var rv = constructor.apply(obj, args);
- if (typeof rv === 'object') {
- obj = rv;
+
+ if (argCount === 0) {
+ return newer(handle, argTypes);
+ } else if (argCount === 1) {
+ return newer(handle, argTypes, arguments[3]);
+ } else if (argCount === 2) {
+ return newer(handle, argTypes, arguments[3], arguments[4]);
+ } else if (argCount === 3) {
+ return newer(handle, argTypes, arguments[3], arguments[4], arguments[5]);
+ } else if (argCount === 4) {
+ return newer(handle, argTypes, arguments[3], arguments[4], arguments[5], arguments[6]);
+ } else {
+ // This is a slow path! (.apply and .splice are slow), so a few specializations are present above.
+ return newer.apply(null, arguments.splice(1));
}
-*/
- return __emval_register(obj);
}
// appease jshint (technically this code uses eval)
@@ -192,38 +219,62 @@ function parseParameters(argCount, argTypes, argWireTypes) {
function __emval_call(handle, argCount, argTypes) {
requireHandle(handle);
+ var types = lookupTypes(argCount, argTypes);
+
+ var args = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ args[i] = types[i].fromWireType(arguments[3 + i]);
+ }
+
var fn = _emval_handle_array[handle].value;
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 3));
var rv = fn.apply(undefined, args);
return __emval_register(rv);
}
-function __emval_call_method(handle, name, argCount, argTypes) {
- requireHandle(handle);
- name = getStringOrSymbol(name);
-
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 4));
- var obj = _emval_handle_array[handle].value;
- var rv = obj[name].apply(obj, args);
- return __emval_register(rv);
+function lookupTypes(argCount, argTypes, argWireTypes) {
+ var a = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ a[i] = requireRegisteredType(
+ HEAP32[(argTypes >> 2) + i],
+ "parameter " + i);
+ }
+ return a;
}
-function __emval_call_void_method(handle, name, argCount, argTypes) {
- requireHandle(handle);
- name = getStringOrSymbol(name);
+function __emval_get_method_caller(argCount, argTypes) {
+ var types = lookupTypes(argCount, argTypes);
+
+ var retType = types[0];
+ var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$";
+
+ var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"];
+ var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType];
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 4));
- var obj = _emval_handle_array[handle].value;
- obj[name].apply(obj, args);
+ var argsList = ""; // 'arg0, arg1, arg2, ... , argN'
+ var argsListWired = ""; // 'arg0Wired, ..., argNWired'
+ for (var i = 0; i < argCount - 1; ++i) {
+ argsList += (i !== 0 ? ", " : "") + "arg" + i;
+ argsListWired += ", arg" + i + "Wired";
+ args1.push("argType" + i);
+ args2.push(types[1 + i]);
+ }
+
+ var invokerFnBody =
+ "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" +
+ "requireHandle(handle);\n" +
+ "name = getStringOrSymbol(name);\n";
+
+ for (var i = 0; i < argCount - 1; ++i) {
+ invokerFnBody += "var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n";
+ }
+ invokerFnBody +=
+ "var obj = _emval_handle_array[handle].value;\n" +
+ "return retType.toWireType(null, obj[name](" + argsList + "));\n" +
+ "}));\n";
+
+ args1.push(invokerFnBody);
+ var invokerFunction = new_(Function, args1).apply(null, args2);
+ return invokerFunction;
}
function __emval_has_function(handle, name) {
diff --git a/src/intertyper.js b/src/intertyper.js
index 445c37f4..6da30ae8 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -537,12 +537,17 @@ function intertyper(data, sidePass, baseLineNums) {
});
}
} else if (!external) {
- if (item.tokens[3].text == 'c')
- item.tokens.splice(3, 1);
- if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) {
- ret.value = parseLLVMFunctionCall(item.tokens.slice(2));
+ if (item.tokens[3] && item.tokens[3].text != ';') {
+ if (item.tokens[3].text == 'c') {
+ item.tokens.splice(3, 1);
+ }
+ if (item.tokens[3].text in PARSABLE_LLVM_FUNCTIONS) {
+ ret.value = parseLLVMFunctionCall(item.tokens.slice(2));
+ } else {
+ ret.value = scanConst(item.tokens[3], ret.type);
+ }
} else {
- ret.value = scanConst(item.tokens[3], ret.type);
+ ret.value = { intertype: 'value', ident: '0', value: '0', type: ret.type };
}
}
return [ret];
diff --git a/src/jsifier.js b/src/jsifier.js
index 3f52337f..77aff895 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -12,6 +12,8 @@ var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume');
var addedLibraryItems = {};
var asmLibraryFunctions = [];
+var SETJMP_LABEL = -1;
+
// JSifier
function JSify(data, functionsOnly, givenFunctions) {
var mainPass = !functionsOnly;
@@ -730,7 +732,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}).join('\n') + '\n';
if (func.setjmpTable && ASM_JS) {
// emit a label in which we write to the proper local variable, before jumping to the actual label
- ret += ' case -1111: ';
+ ret += ' case ' + SETJMP_LABEL + ': ';
ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
}).join(' else ');
@@ -1030,13 +1032,13 @@ function JSify(data, functionsOnly, givenFunctions) {
}
for (var i = 0; i < idents.length; i++) {
if (keys(deps[idents[i]]).length == 0) {
- pre = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + pre;
+ post = 'var ' + idents[i] + ' = ' + valueJSes[idents[i]] + ';' + post;
remove(idents[i]);
continue mainLoop;
}
}
// If we got here, we have circular dependencies, and must break at least one.
- pre = 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';' + pre;
+ pre += 'var ' + idents[0] + '$phi = ' + valueJSes[idents[0]] + ';';
post += 'var ' + idents[0] + ' = ' + idents[0] + '$phi;';
remove(idents[0]);
}
@@ -1160,6 +1162,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return ret + ';';
});
makeFuncLineActor('resume', function(item) {
+ if (DISABLE_EXCEPTION_CATCHING) return 'abort()';
if (item.ident == 0) {
// No exception to resume, so we can just bail.
// This is related to issue #917 and http://llvm.org/PR15518
@@ -1181,6 +1184,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (disabled) {
ret = call_ + ';';
} else if (ASM_JS) {
+ call_ = call_.replace('; return', ''); // we auto-add returns when aborting, but do not need them here
ret = '(__THREW__ = 0,' + call_ + ');';
} else {
ret = '(function() { try { __THREW__ = 0; return '
@@ -1356,7 +1360,7 @@ function JSify(data, functionsOnly, givenFunctions) {
var ignoreFunctionIndexizing = [];
var useJSArgs = (simpleIdent + '__jsargs') in LibraryManager.library;
var hasVarArgs = isVarArgsFunctionType(type);
- var normalArgs = (hasVarArgs && !useJSArgs) ? countNormalArgs(type) : -1;
+ var normalArgs = (hasVarArgs && !useJSArgs) ? countNormalArgs(type, null, true) : -1;
var byPointer = getVarData(funcData, ident);
var byPointerForced = false;
@@ -1487,9 +1491,9 @@ function JSify(data, functionsOnly, givenFunctions) {
}
if (ASM_JS && funcData.setjmpTable) {
- // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to -111 to handle it.
+ // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to a special label to handle it.
// otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way
- ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n';
+ ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) != 0)) { setjmpLabel = ' + asmCoercion('_testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable)', 'i32') + '; if ((setjmpLabel|0) > 0) { label = ' + SETJMP_LABEL + '; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n';
}
return ret;
diff --git a/src/library.js b/src/library.js
index 344ec8d6..e65754ba 100644
--- a/src/library.js
+++ b/src/library.js
@@ -619,7 +619,7 @@ LibraryManager.library = {
#endif
allocate([ allocate(
{{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}},
- 'void*', ALLOC_DYNAMIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}});
+ 'void*', ALLOC_NORMAL) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}});
},
quit: function() {
@@ -3576,14 +3576,14 @@ LibraryManager.library = {
return -1;
},
fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES',
- '_scanString', 'getc', 'ungetc'],
+ '_scanString', 'fgetc', 'fseek', 'ftell'],
fscanf: function(stream, format, varargs) {
// int fscanf(FILE *restrict stream, const char *restrict format, ... );
// http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
if (FS.streams[stream]) {
- var stack = [];
- var get = function() { var ret = _fgetc(stream); stack.push(ret); return ret };
- var unget = function(c) { return _ungetc(stack.pop(), stream) };
+ var i = _ftell(stream), SEEK_SET = 0;
+ var get = function () { i++; return _fgetc(stream); };
+ var unget = function () { _fseek(stream, --i, SEEK_SET); };
return __scanString(format, get, unget, varargs);
} else {
return -1;
@@ -3726,30 +3726,44 @@ LibraryManager.library = {
* this implementation simply uses malloc underneath the call to
* mmap.
*/
+ var MAP_PRIVATE = 2;
+ var allocated = false;
+
if (!_mmap.mappings) _mmap.mappings = {};
+
if (stream == -1) {
var ptr = _malloc(num);
+ if (!ptr) return -1;
+ _memset(ptr, 0, num);
+ allocated = true;
} else {
var info = FS.streams[stream];
if (!info) return -1;
var contents = info.object.contents;
- contents = Array.prototype.slice.call(contents, offset, offset+num);
- ptr = allocate(contents, 'i8', ALLOC_NORMAL);
- }
- // align to page size
- var ret = ptr;
- if (ptr % PAGE_SIZE != 0) {
- var old = ptr;
- ptr = _malloc(num + PAGE_SIZE);
- ret = alignMemoryPage(ptr);
- _memcpy(ret, old, num);
- _free(old);
- }
- if (stream == -1) {
- _memset(ret, 0, num);
+ // Only make a new copy when MAP_PRIVATE is specified.
+ if (flags & MAP_PRIVATE == 0) {
+ // We can't emulate MAP_SHARED when the file is not backed by HEAP.
+ assert(contents.buffer === HEAPU8.buffer);
+ ptr = contents.byteOffset;
+ allocated = false;
+ } else {
+ // Try to avoid unnecessary slices.
+ if (offset > 0 || offset + num < contents.length) {
+ if (contents.subarray) {
+ contents = contents.subarray(offset, offset+num);
+ } else {
+ contents = Array.prototype.slice.call(contents, offset, offset+num);
+ }
+ }
+ ptr = _malloc(num);
+ if (!ptr) return -1;
+ HEAPU8.set(contents, ptr);
+ allocated = true;
+ }
}
- _mmap.mappings[ret] = { malloc: ptr, num: num };
- return ret;
+
+ _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated };
+ return ptr;
},
__01mmap64_: 'mmap',
@@ -3760,7 +3774,9 @@ LibraryManager.library = {
if (!info) return 0;
if (num == info.num) {
_mmap.mappings[start] = null;
- _free(info.malloc);
+ if (info.allocated) {
+ _free(info.malloc);
+ }
}
return 0;
},
@@ -3902,7 +3918,14 @@ LibraryManager.library = {
str++;
}
}
- }
+ } else if (finalBase==16) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
+ if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
+ {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
+ str += 2;
+ }
+ }
+ }
if (!finalBase) finalBase = 10;
// Get digits.
@@ -3953,13 +3976,14 @@ LibraryManager.library = {
#if USE_TYPED_ARRAYS == 2
_parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }],
_parseInt64: function(str, endptr, base, min, max, unsign) {
- var start = str;
+ var isNegative = false;
// Skip space.
while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++;
-
+
// Check for a plus/minus sign.
if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) {
str++;
+ isNegative = true;
} else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) {
str++;
}
@@ -3975,12 +3999,19 @@ LibraryManager.library = {
str += 2;
} else {
finalBase = 8;
- str++;
ok = true; // we saw an initial zero, perhaps the entire thing is just "0"
}
}
- }
+ } else if (finalBase==16) {
+ if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
+ if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
+ {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
+ str += 2;
+ }
+ }
+ }
if (!finalBase) finalBase = 10;
+ start = str;
// Get digits.
var chr;
@@ -3993,6 +4024,7 @@ LibraryManager.library = {
ok = true;
}
}
+
if (!ok) {
___setErrNo(ERRNO_CODES.EINVAL);
{{{ makeStructuralReturn(['0', '0']) }}};
@@ -4004,7 +4036,8 @@ LibraryManager.library = {
}
try {
- i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign);
+ var numberString = isNegative ? '-'+Pointer_stringify(start, str - start) : Pointer_stringify(start, str - start);
+ i64Math.fromString(numberString, finalBase, min, max, unsign);
} catch(e) {
___setErrNo(ERRNO_CODES.ERANGE); // not quite correct
}
@@ -5072,7 +5105,13 @@ LibraryManager.library = {
return _malloc(size);
},
__cxa_free_exception: function(ptr) {
- return _free(ptr);
+ try {
+ return _free(ptr);
+ } catch(e) { // XXX FIXME
+#if ASSERTIONS
+ Module.printErr('exception during cxa_free_exception: ' + e);
+#endif
+ }
},
__cxa_throw__sig: 'viii',
__cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'],
@@ -6170,8 +6209,9 @@ LibraryManager.library = {
clock_gettime__deps: ['__timespec_struct_layout'],
clock_gettime: function(clk_id, tp) {
// int clock_gettime(clockid_t clk_id, struct timespec *tp);
- {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}}
- {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
+ var now = Date.now();
+ {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds
+ {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported
return 0;
},
clock_settime: function(clk_id, tp) {
@@ -6794,19 +6834,17 @@ LibraryManager.library = {
26: 'Text file busy',
18: 'Invalid cross-device link'
},
+ __errno_state: 0,
+ __setErrNo__deps: ['__errno_state'],
+ __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};',
__setErrNo: function(value) {
// For convenient setting and returning of errno.
- if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL);
- {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}}
+ {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}}
return value;
},
__errno_location__deps: ['__setErrNo'],
__errno_location: function() {
- if (!___setErrNo.ret) {
- ___setErrNo.ret = allocate([0], 'i32', ALLOC_NORMAL);
- {{{ makeSetValue('___setErrNo.ret', '0', '0', 'i32') }}}
- }
- return ___setErrNo.ret;
+ return ___errno_state;
},
__errno: '__errno_location',
@@ -7563,7 +7601,7 @@ LibraryManager.library = {
},
emscripten_run_script_int: function(ptr) {
- return eval(Pointer_stringify(ptr));
+ return eval(Pointer_stringify(ptr))|0;
},
emscripten_run_script_string: function(ptr) {
diff --git a/src/library_browser.js b/src/library_browser.js
index 5f9c4d06..1a79a49b 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -184,7 +184,7 @@ mergeInto(LibraryManager.library, {
};
audio.src = url;
// workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror
- setTimeout(function() {
+ Browser.safeSetTimeout(function() {
finish(audio); // try to use it even though it is not necessarily ready to play
}, 10000);
} else {
@@ -201,7 +201,8 @@ mergeInto(LibraryManager.library, {
canvas['webkitRequestPointerLock'];
canvas.exitPointerLock = document['exitPointerLock'] ||
document['mozExitPointerLock'] ||
- document['webkitExitPointerLock'];
+ document['webkitExitPointerLock'] ||
+ function(){}; // no-op if function does not exist
canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
function pointerLockChange() {
@@ -360,6 +361,30 @@ mergeInto(LibraryManager.library, {
window.requestAnimationFrame(func);
},
+ // generic abort-aware wrapper for an async callback
+ safeCallback: function(func) {
+ return function() {
+ if (!ABORT) return func.apply(null, arguments);
+ };
+ },
+
+ // abort-aware versions
+ safeRequestAnimationFrame: function(func) {
+ Browser.requestAnimationFrame(function() {
+ if (!ABORT) func();
+ });
+ },
+ safeSetTimeout: function(func, timeout) {
+ setTimeout(function() {
+ if (!ABORT) func();
+ }, timeout);
+ },
+ safeSetInterval: function(func, timeout) {
+ setInterval(function() {
+ if (!ABORT) func();
+ }, timeout);
+ },
+
getMovementX: function(event) {
return event['movementX'] ||
event['mozMovementX'] ||
@@ -611,7 +636,7 @@ mergeInto(LibraryManager.library, {
Module['noExitRuntime'] = true;
// TODO: cache these to avoid generating garbage