aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js88
-rw-r--r--src/compiler.js2
-rw-r--r--[-rwxr-xr-x]src/embind/embind.js99
-rw-r--r--[-rwxr-xr-x]src/embind/emval.js177
-rw-r--r--src/jsifier.js1
-rw-r--r--src/library.js160
-rw-r--r--src/library_browser.js53
-rw-r--r--src/library_egl.js4
-rw-r--r--src/library_gl.js87
-rw-r--r--src/library_glfw.js29
-rw-r--r--src/library_glut.js41
-rw-r--r--src/library_sdl.js127
-rw-r--r--src/modules.js46
-rw-r--r--src/parseTools.js25
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js4
-rw-r--r--src/settings.js14
17 files changed, 666 insertions, 295 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 03d44cb7..2cc46ab6 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -20,6 +20,7 @@ var BRANCH_INVOKE = set('branch', 'invoke');
var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
+var I64_DOUBLE_FLIP = { i64: 'double', double: 'i64' };
// Analyzer
@@ -99,7 +100,86 @@ function analyzer(data, sidePass) {
}
}
delete item.items;
+ this.forwardItem(item, 'CastAway');
+ }
+ });
+
+ // CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily
+ // (load a double, convert to i64, use as i64).
+ // We optimize this by checking if there are such bitcasts. If so we create a shadow
+ // variable that is of the other type, and use that in the relevant places. (As SSA, this is valid, and
+ // variable elimination later will remove the double load if it is no longer needed.)
+ //
+ // Note that aside from being an optimization, this is needed for correctness in some cases: If code
+ // assumes it can bitcast a double to an i64 and back and forth without loss, that may be violated
+ // due to NaN canonicalization.
+ substrate.addActor('CastAway', {
+ processItem: function(item) {
this.forwardItem(item, 'Legalizer');
+ if (USE_TYPED_ARRAYS != 2) return;
+
+ item.functions.forEach(function(func) {
+ var has = false;
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP) {
+ has = true;
+ }
+ }
+ });
+ if (!has) return;
+ // there are i64<-->double bitcasts, create shadows for everything
+ var shadowed = {};
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ var i = 0;
+ while (i < lines.length) {
+ var lines = label.lines;
+ var line = lines[i];
+ if (line.intertype == 'load' && line.type in I64_DOUBLE_FLIP) {
+ if (line.pointer.intertype != 'value') { i++; continue } // TODO
+ shadowed[line.assignTo] = 1;
+ var shadow = line.assignTo + '$$SHADOW';
+ var flip = I64_DOUBLE_FLIP[line.type];
+ lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
+ tokens: null,
+ indent: 2,
+ lineNum: line.lineNum + 0.5,
+ assignTo: shadow,
+ intertype: 'load',
+ pointerType: flip + '*',
+ type: flip,
+ valueType: flip,
+ pointer: {
+ intertype: 'value',
+ ident: line.pointer.ident,
+ type: flip + '*'
+ },
+ align: line.align,
+ ident: line.ident
+ });
+ // note: no need to update func.lines, it is generated in a later pass
+ i++;
+ }
+ i++;
+ }
+ });
+ // use shadows where possible
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP && line.ident in shadowed) {
+ var shadow = line.ident + '$$SHADOW';
+ line.params[0].ident = shadow;
+ line.params[0].type = line.type;
+ line.type2 = line.type;
+ }
+ }
+ });
+ });
}
});
@@ -174,12 +254,15 @@ function analyzer(data, sidePass) {
var factor = (next - prev)/(4*toAdd.length+3);
for (var k = 0; k < toAdd.length; k++) {
toAdd[k].lineNum = prev + ((k+1)*factor);
+ assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum);
}
}
function removeAndAdd(lines, i, toAdd) {
var item = lines[i];
interpLines(lines, i, toAdd);
Array.prototype.splice.apply(lines, [i, 1].concat(toAdd));
+ if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum);
+ if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum);
return toAdd.length;
}
function legalizeFunctionParameters(params) {
@@ -328,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/compiler.js b/src/compiler.js
index 8b9606f1..94e77e26 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -183,8 +183,8 @@ if (ASM_JS) {
assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap');
assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2');
}
-assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals
assert(!BUILD_AS_SHARED_LIB, 'shared libs are deprecated');
+//assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals');
// Output some info and warnings based on settings
diff --git a/src/embind/embind.js b/src/embind/embind.js
index cadee700..91386c69 100755..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 100755..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/jsifier.js b/src/jsifier.js
index 466ed193..c1bf1e1a 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1463,6 +1463,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (!byPointerForced && !funcData.setjmpTable) {
// normal asm function pointer call
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
+ Functions.neededTables[sig] = 1;
} else {
// This is a call through an invoke_*, either a forced one, or a setjmp-required one
// note: no need to update argsTypes at this point
diff --git a/src/library.js b/src/library.js
index cfe83c6e..caf1dd0b 100644
--- a/src/library.js
+++ b/src/library.js
@@ -296,74 +296,97 @@ LibraryManager.library = {
if (typeof XMLHttpRequest !== 'undefined') {
if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
// Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
- var LazyUint8Array = function(chunkSize, length) {
- this.length = length;
- this.chunkSize = chunkSize;
+ var LazyUint8Array = function() {
+ this.lengthKnown = false;
this.chunks = []; // Loaded chunks. Index is the chunk number
}
LazyUint8Array.prototype.get = function(idx) {
if (idx > this.length-1 || idx < 0) {
return undefined;
}
- var chunkOffset = idx % chunkSize;
- var chunkNum = Math.floor(idx / chunkSize);
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = Math.floor(idx / this.chunkSize);
return this.getter(chunkNum)[chunkOffset];
}
LazyUint8Array.prototype.setDataGetter = function(getter) {
this.getter = getter;
}
-
- // Find length
- var xhr = new XMLHttpRequest();
- xhr.open('HEAD', url, false);
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- var datalength = Number(xhr.getResponseHeader("Content-length"));
- var header;
- var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+
+ LazyUint8Array.prototype.cacheLength = function() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
#if SMALL_XHR_CHUNKS
- var chunkSize = 1024; // Chunk size in bytes
+ var chunkSize = 1024; // Chunk size in bytes
#else
- var chunkSize = 1024*1024; // Chunk size in bytes
+ var chunkSize = 1024*1024; // Chunk size in bytes
#endif
- if (!hasByteServing) chunkSize = datalength;
-
- // Function to get a range from the remote URL.
- var doXHR = (function(from, to) {
- if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
- if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
-
- // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, false);
- if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
-
- // Some hints to the browser that we want binary data.
- if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- }
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum+1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ }
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- if (xhr.response !== undefined) {
- return new Uint8Array(xhr.response || []);
- } else {
- return intArrayFromString(xhr.responseText || '', true);
- }
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperty(lazyArray, "length", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
});
-
- var lazyArray = new LazyUint8Array(chunkSize, datalength);
- lazyArray.setDataGetter(function(chunkNum) {
- var start = chunkNum * lazyArray.chunkSize;
- var end = (chunkNum+1) * lazyArray.chunkSize - 1; // including this byte
- end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
- lazyArray.chunks[chunkNum] = doXHR(start, end);
- }
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
- return lazyArray.chunks[chunkNum];
+ Object.defineProperty(lazyArray, "chunkSize", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
});
+
var properties = { isDevice: false, contents: lazyArray };
} else {
var properties = { isDevice: false, url: url };
@@ -2465,9 +2488,15 @@ LibraryManager.library = {
__scanString.whiteSpace[{{{ charCode(' ') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\t') }}}] = 1;
__scanString.whiteSpace[{{{ charCode('\n') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1;
+ __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1;
__scanString.whiteSpace[' '] = 1;
__scanString.whiteSpace['\t'] = 1;
__scanString.whiteSpace['\n'] = 1;
+ __scanString.whiteSpace['\v'] = 1;
+ __scanString.whiteSpace['\f'] = 1;
+ __scanString.whiteSpace['\r'] = 1;
}
// Supports %x, %4x, %d.%d, %lld, %s, %f, %lf.
// TODO: Support all format specifiers.
@@ -2840,7 +2869,7 @@ LibraryManager.library = {
} else if (next == {{{ charCode('o') }}}) {
argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8);
} else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) {
- prefix = flagAlternative ? '0x' : '';
+ prefix = (flagAlternative && currArg != 0) ? '0x' : '';
#if PRECISE_I64_MATH
if (argSize == 8 && i64Math) {
if (origArg[1]) {
@@ -3453,7 +3482,8 @@ LibraryManager.library = {
} else if (oldObj.isRoot || oldObj.path == FS.currentPath) {
___setErrNo(ERRNO_CODES.EBUSY);
return -1;
- } else if (newObj.path && newObj.path.indexOf(oldObj.path) == 0) {
+ } else if (newObj.parentPath &&
+ newObj.parentPath.indexOf(oldObj.path) == 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
} else if (newObj.exists && newObj.object.isFolder) {
@@ -3546,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;
@@ -5105,7 +5135,7 @@ LibraryManager.library = {
}
// Clear state flag.
#if ASM_JS
- asm.setThrew(0);
+ asm['setThrew'](0);
#else
__THREW__ = 0;
#endif
@@ -6305,7 +6335,7 @@ LibraryManager.library = {
#endif
longjmp: function(env, value) {
#if ASM_JS
- asm.setThrew(env, value || 1);
+ asm['setThrew'](env, value || 1);
throw 'longjmp';
#else
throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
@@ -6764,19 +6794,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);',
__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') }}}
+ {{{ m