Merge branch 'incoming' into proxyGL

This commit is contained in:
Alon Zakai 2014-06-16 14:40:45 -07:00
Родитель 7acb488261 7c26bbdb7d
Коммит ba387574f2
31 изменённых файлов: 2538 добавлений и 308 удалений

Просмотреть файл

@ -144,4 +144,4 @@ a license to everyone to use it as detailed in LICENSE.)
* Jason Green <jason@transgaming.com> (copyright owned by TransGaming, Inc.)
* Ningxin Hu <ningxin.hu@intel.com> (copyright owned by Intel)
* Nicolas Guillemot <nlguillemot@gmail.com>
* Sathyanarayanan Gunasekaran <gsathya.ceg@gmail.com> (copyright owned by Mozilla Foundation)

141
ChangeLog
Просмотреть файл

@ -10,9 +10,144 @@ Not all changes are documented here. In particular, new features, user-oriented
Current trunk code
------------------
- To see a list of commits in the active development branch 'incoming', which have not yet been packaged in a release, see
- Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...incoming
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...incoming
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...incoming
- Emscripten: https://github.com/kripken/emscripten/compare/1.20.0...incoming
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.20.0...incoming
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.20.0...incoming
v1.20.0: 6/13/2014
------------------
- Optimize in-memory virtual filesystem performance when serialized to an IndexedDB.
- Fixed memcpy regression with ta0 and ta1 modes.
- Fixed an issue with line numbers being messed up when generating source maps (#2410)
- Fixed an ffdb logging bug that could cause it to drop messages if they were being received too fast. Added support getting memory and system descriptions with ffdb.
- Added a new extension to SDL "emscripten_SDL_SetEventHandler()" which enabled application to perform SDL event handling inside a JS event handler to overcome browser security restrictions. (#2417)
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.2...1.20.0
- Emscripten-LLVM: no changes.
- Emscripten-Clang: no changes.
v1.19.2: 6/9/2014
------------------
- Updated CMake support for response file handling.
- Fixed issues with glfwGetProcAddress and glfwSetWindowSizeCallback.
- Fixed an issue with regexes that caused issues on IE11 runtime (#2400)
- Added a new functions emscripten_get_preloaded_image_data() and emscripten_get_preloaded_image_data_from_FILE() to obtain pixel data of preloaded images.
- Greatly improved ffdb capabilities to operate a FFOS device.
- Fixed a Windows-specific bug where the user temp directory was littered with temporary .rsp files that did not get cleaned up.
- Improved SIMD support.
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.1...1.19.2
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.19.1...1.19.2
- Emscripten-Clang: no changes.
v1.19.1: 6/3/2014
------------------
- Migrate to using musl sscanf and sprintf and the family that writes to memory, and not directly to the filesystem.
- Improve the error messages from -s SAFE_HEAP_ACCESS=1 runtime checks.
- Added new linker flag -s NO_DYNAMIC_EXECUTION=1 which removes the use of eval() and new Function() in the generated output. For more information, see "Eval and related functions are disabled" in https://developer.chrome.com/extensions/contentSecurityPolicy .
- Fixed a compiler issue when very large double constants are present. (#2392)
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.19.0...1.19.1
- Emscripten-LLVM: no changes.
- Emscripten-Clang: no changes.
v1.19.0: 5/29/2014
------------------
- Added an error message to signal that linkable modules are not supported in fastcomp.
- Fixed a miscompilation issue that resulted in an error "SyntaxError: invalid increment operand" and a statement +(+0) being generated (#2314)
- Make optimized compiler output smaller by running the shell code through uglify when not using closure.
- Fixed a crash in SDL audio loading code introduced in v1.18.3
- Fixed an issue where glTex(Sub)Image2D might throw an exception on error, instead of setting glGetError().
- Added new typedefs emscripten_align1_short, emscripten_align{1/2}_int, emscripten_align{1/2}_float and emscripten_align{1/2/4}_double to ease signaling the compiler that unaligned data is present. (#2378)
- Fixed an embind issue with refcount tracking on smart pointers.
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.4...1.19.0
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.4...1.19.0
- Emscripten-Clang: no changes.
v1.18.4: 5/27/2014
------------------
- Fixed error message on unsupported linking options (#2365)
- Updated embind to latest version from IMVU upstream.
- Fixed an issue where source maps did not load properly in Firefox.
- Added a more descriptive error message to fastcomp when MAX_SETJMPS limit is violated. (#2379)
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.3...1.18.4
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.3...1.18.4
- Emscripten-Clang: no changes.
v1.18.3: 5/21/2014
------------------
- Added support to emcc command line for "archive groups": -Wl,--start-group and -Wl,--end-group
- Greatly optimized ccall and cwrap implementations.
- Added new support for SDL_Mix backend to use WebAudio to play back audio clips.
- Fixed a registerizeHarder issue with elimination of conditional expressions.
- Migrated single-character standard C functions (islower, tolower, and the family) to use musl implementations.
- Updated relooper to not optimize out breaks if it causes excessive nesting.
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.2...1.18.3
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.2...1.18.3
- Emscripten-Clang: no changes.
v1.18.2: 5/19/2014
------------------
- Fixed a problem which blocked user applications from handling WebGL context loss events themselves.
- Added a new HTML5 api function emscripten_is_webgl_context_lost() which allows polling for context loss in addition to receiving events.
- Improved async wget progress events to work better across browsers.
- Improved WebIDL binder support.
- Added new typeof() function to emscripten::val.
- Added support for SDL window events SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST, SDL_WINDOWEVENT_SHOWN, SDL_WINDOWEVENT_HIDDEN.
- Fixed a compiler miscompilation on unsigned i1 bitcasts (#2350)
- Fixed a compiler bug where doubles in varargs might not get 8-byte aligned (#2358)
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.1...1.18.2
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.18.1...1.18.2
- Emscripten-Clang: no changes.
v1.18.1: 5/12/2014
------------------
- Fixed an issue where the mouse wheel scroll did not work with SDL.
- Fixed an issue with emscripten_async_wget, which undesirably expected that the string pointer passed to it stayed alive for the duration of the operation (#2349)
- Emscripten now issues a warning message when the EXPORTED_FUNCTIONS list contains invalid symbol names (#2338)
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.18.0...1.18.1
- Emscripten-LLVM: no changes.
- Emscripten-Clang: no changes.
v1.18.0: 5/10/2014
------------------
- Enable support for low-level C<->JS interop to marshall 64 bit integers from C to JS.
- Fixed an issue that caused some programs to immediately run out of memory "(cannot enlarge memory arrays)" at startup. (#2334)
- Fixed a crash issue with generated touch events that didn't correspond to a real touch.
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.17.0...1.18.0
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.17.0...1.18.0
- Emscripten-Clang: no changes.
v1.17.0: 5/6/2014
------------------
- Enabled asm.js compilation and -s PRECISE_F32 support when using embind.
- Improved relooper to emit switches in many-entried blocks.
- Fixed a GLFW bug where mouse wheel direction was reversed.
- Fixed glfwGetKey to work even when no callback is registered with glfwGetKeyCallback (#1320)
- Added a new tool 'webidl_binder' that generates C <-> JS interop code from WebIDL descriptions.
- Fix emscripten compilation to work on pages that don't contain a HTML canvas.
- Added a new error message to default shell when an uncaught exception is thrown.
- Improved error diagnostics reported by -s SAFE_HEAP=1.
- Added support for registering callbacks hook to VFS file open, write, move, close and delete.
- Added embind support to std::basic_string<unsigned char>
- By default, the C runtime will no longer exit after returning from main() when safeSetTimeout() or safeSetInterval() is used.
- Fixed an issue with sscanf formatting (#2322)
- Fixed an issue where precompiled headers were given a wrong output filename (#2320)
- Enabled registerizeHarder optimization pass to work when outlining is enabled.
- Fixed an issue with strptime month handling (#2324)
- Added an initial implementation of a new tool 'ffdb' which can be used to operate a Firefox OS phone from the command line.
- Fixed a compiler crash on assertion failure '!contains(BranchesOut, Target)' (emscripten-fastcomp #32)
- Added a new ABI to Clang that targets Emscripten specifically. Stop aligning member functions to save some space in the function table array.
- Full list of changes:
- Emscripten: https://github.com/kripken/emscripten/compare/1.16.0...1.17.0
- Emscripten-LLVM: https://github.com/kripken/emscripten-fastcomp/compare/1.16.0...1.17.0
- Emscripten-Clang: https://github.com/kripken/emscripten-fastcomp-clang/compare/1.16.0...1.17.0
v1.16.0: 4/16/2014
------------------

4
emcc
Просмотреть файл

@ -1376,6 +1376,8 @@ try:
if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
assert not (bind and shared.Settings.NO_DYNAMIC_EXECUTION), 'NO_DYNAMIC_EXECUTION disallows embind'
if proxy_to_worker:
shared.Settings.PROXY_TO_WORKER = 1
@ -1692,7 +1694,7 @@ try:
logging.debug('wrote memory initialization to %s', memfile)
else:
logging.debug('did not see memory initialization')
elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE:
elif shared.Settings.USE_TYPED_ARRAYS == 2 and not shared.Settings.MAIN_MODULE and not shared.Settings.SIDE_MODULE and debug_level < 4:
# not writing a binary init, but we can at least optimize them by splitting them up
src = open(final).read()
src = shared.JS.optimize_initializer(src)

Просмотреть файл

@ -1,2 +1,2 @@
1.19.1
1.20.0

Просмотреть файл

@ -3,6 +3,7 @@
"SDL_Init": ["malloc", "free"],
"SDL_GL_GetProcAddress": ["emscripten_GetProcAddress"],
"eglGetProcAddress": ["emscripten_GetProcAddress"],
"glfwGetProcAddress": ["emscripten_GetProcAddress"],
"emscripten_GetProcAddress": ["strstr"]
}

Просмотреть файл

@ -8,13 +8,14 @@ var TIME = 10000;
Date.now = function() {
return TIME++;
};
performance.now = Date.now;
if (typeof performance === 'object') performance.now = Date.now;
function hashMemory(id) {
var ret = 0;
for (var i = 0; i < HEAPU8.length; i++) {
var len = Math.max(DYNAMICTOP, STATICTOP);
for (var i = 0; i < len; i++) {
ret = (ret*17 + HEAPU8[i])|0;
}
print(id + ':' + ret);
printErr(id + ':' + ret);
}

Просмотреть файл

@ -370,7 +370,7 @@ function JSify(data, functionsOnly) {
// name the function; overwrite if it's already named
snippet = snippet.replace(/function(?:\s+([^(]+))?\s*\(/, 'function _' + ident + '(');
if (LIBRARY_DEBUG) {
if (LIBRARY_DEBUG && !LibraryManager.library[ident + '__asm']) {
snippet = snippet.replace('{', '{ var ret = (function() { if (Runtime.debug) Module.printErr("[library call:' + ident + ': " + Array.prototype.slice.call(arguments).map(Runtime.prettyPrint) + "]"); ');
snippet = snippet.substr(0, snippet.length-1) + '}).apply(this, arguments); if (Runtime.debug && typeof ret !== "undefined") Module.printErr(" [ return:" + Runtime.prettyPrint(ret)); return ret; \n}';
}
@ -1902,6 +1902,9 @@ function JSify(data, functionsOnly) {
print(read('webGLWorker.js'));
print(read('proxyWorker.js'));
}
if (DETERMINISTIC) {
print(read('deterministic.js'));
}
if (RUNTIME_TYPE_INFO) {
Types.cleanForRuntime();
print('Runtime.typeInfo = ' + JSON.stringify(Types.types));

Просмотреть файл

@ -3166,7 +3166,7 @@ LibraryManager.library = {
#endif
environ: 'allocate(1, "i32*", ALLOC_STATIC)',
__environ__deps: ['environ'],
__environ: '_environ',
__environ: 'environ',
__buildEnvironment__deps: ['__environ'],
__buildEnvironment: function(env) {
// WARNING: Arbitrary limit!
@ -3388,9 +3388,20 @@ LibraryManager.library = {
memcpy__sig: 'iiii',
memcpy__deps: ['emscripten_memcpy_big'],
memcpy: function(dest, src, num) {
#if USE_TYPED_ARRAYS == 0
{{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
return num;
#endif
#if USE_TYPED_ARRAYS == 1
{{{ makeCopyValues('dest', 'src', 'num', 'null') }}};
return num;
#endif
dest = dest|0; src = src|0; num = num|0;
var ret = 0;
#if USE_TYPED_ARRAYS
if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0;
#endif
ret = dest|0;
if ((dest&3) == (src&3)) {
while (dest & 3) {

Просмотреть файл

@ -1175,6 +1175,39 @@ mergeInto(LibraryManager.library, {
var info = Browser.workers[id];
if (!info) return -1;
return info.awaited;
},
emscripten_get_preloaded_image_data: function(path, w, h) {
if (typeof path === "number") {
path = Pointer_stringify(path);
}
path = PATH.resolve(path);
var canvas = Module["preloadedImages"][path];
if (canvas) {
var ctx = canvas.getContext("2d");
var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
var buf = _malloc(canvas.width * canvas.height * 4);
HEAPU8.set(image.data, buf);
{{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}};
{{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}};
return buf;
}
return 0;
},
emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'],
emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) {
var stream = FS.getStreamFromPtr(file);
if (stream) {
return _emscripten_get_preloaded_image_data(stream.path, w, h);
}
return 0;
}
});

Просмотреть файл

@ -1077,6 +1077,10 @@ mergeInto(LibraryManager.library, {
if (!stream.stream_ops.write) {
throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
}
if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
// seek to the end before writing in append mode
FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
}
var seeking = true;
if (typeof position === 'undefined') {
position = stream.position;
@ -1084,10 +1088,6 @@ mergeInto(LibraryManager.library, {
} else if (!stream.seekable) {
throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
}
if (stream.flags & {{{ cDefine('O_APPEND') }}}) {
// seek to the end before writing in append mode
FS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}});
}
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
if (!seeking) stream.position += bytesWritten;
try {
@ -1480,6 +1480,7 @@ mergeInto(LibraryManager.library, {
// WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
// read() will try to parse UTF8.
obj.contents = intArrayFromString(Module['read'](obj.url), true);
obj.usedBytes = obj.contents.length;
} catch (e) {
success = false;
}
@ -1601,6 +1602,10 @@ mergeInto(LibraryManager.library, {
node.contents = null;
node.url = properties.url;
}
// Add a function that defers querying the file size until it is asked the first time.
Object.defineProperty(node, "usedBytes", {
get: function() { return this.contents.length; }
});
// override each stream op with one that tries to force load the lazy file first
var stream_ops = {};
var keys = Object.keys(node.stream_ops);

Просмотреть файл

@ -417,6 +417,9 @@ var LibraryGLFW = {
glfwSetWindowSizeCallback: function(cbfun) {
GLFW.resizeFunc = cbfun;
if (GLFW.resizeFunc) {
Runtime.dynCall('vii', GLFW.resizeFunc, [Module['canvas'].width, Module['canvas'].height]);
}
},
glfwSetWindowCloseCallback: function(cbfun) {
@ -507,9 +510,9 @@ var LibraryGLFW = {
return Module.ctx.getSupportedExtensions().indexOf(Pointer_stringify(extension)) > -1;
},
glfwGetProcAddress__deps: ['glfwGetProcAddress'],
glfwGetProcAddress__deps: ['emscripten_GetProcAddress'],
glfwGetProcAddress: function(procname) {
return _getProcAddress(procname);
return _emscripten_GetProcAddress(procname);
},
glfwGetGLVersion: function(major, minor, rev) {

Просмотреть файл

@ -135,6 +135,9 @@ mergeInto(LibraryManager.library, {
if (FS.isDir(stat.mode)) {
return callback(null, { timestamp: stat.mtime, mode: stat.mode });
} else if (FS.isFile(stat.mode)) {
// Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array.
// Therefore always convert the file contents to a typed array first before writing the data to IndexedDB.
node.contents = MEMFS.getFileDataAsTypedArray(node);
return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents });
} else {
return callback(new Error('node type not supported'));

Просмотреть файл

@ -2,11 +2,6 @@ mergeInto(LibraryManager.library, {
$MEMFS__deps: ['$FS'],
$MEMFS: {
ops_table: null,
// content modes
CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary)
CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink
CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array
mount: function(mount) {
return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 511 /* 0777 */, 0);
},
@ -71,8 +66,11 @@ mergeInto(LibraryManager.library, {
} else if (FS.isFile(node.mode)) {
node.node_ops = MEMFS.ops_table.file.node;
node.stream_ops = MEMFS.ops_table.file.stream;
node.contents = [];
node.contentMode = MEMFS.CONTENT_FLEXIBLE;
node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.buffer.byteLength which gives the whole capacity.
// When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred
// for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size
// penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.
node.contents = null;
} else if (FS.isLink(node.mode)) {
node.node_ops = MEMFS.ops_table.link.node;
node.stream_ops = MEMFS.ops_table.link.stream;
@ -87,13 +85,88 @@ mergeInto(LibraryManager.library, {
}
return node;
},
ensureFlexible: function(node) {
if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) {
var contents = node.contents;
node.contents = Array.prototype.slice.call(contents);
node.contentMode = MEMFS.CONTENT_FLEXIBLE;
// Given a file node, returns its file data converted to a regular JS array. You should treat this as read-only.
getFileDataAsRegularArray: function(node) {
#if USE_TYPED_ARRAYS == 2
if (node.contents && node.contents.subarray) {
var arr = [];
for (var i = 0; i < node.usedBytes; ++i) arr.push(node.contents[i]);
return arr; // Returns a copy of the original data.
}
#endif
return node.contents; // No-op, the file contents are already in a JS array. Return as-is.
},
#if USE_TYPED_ARRAYS == 2
// Given a file node, returns its file data converted to a typed array.
getFileDataAsTypedArray: function(node) {
if (node.contents && node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.
return new Uint8Array(node.contents);
},
#endif
// Allocates a new backing store for the given node so that it can fit at least newSize amount of bytes.
// May allocate more, to provide automatic geometric increase and amortized linear performance appending writes.
// Never shrinks the storage.
expandFileStorage: function(node, newCapacity) {
#if USE_TYPED_ARRAYS == 2
#if !MEMFS_APPEND_TO_TYPED_ARRAYS
// If we are asked to expand the size of a file that already exists, revert to using a standard JS array to store the file
// instead of a typed array. This makes resizing the array more flexible because we can just .push() elements at the back to
// increase the size.
if (node.contents && node.contents.subarray && newCapacity > node.contents.length) {
node.contents = MEMFS.getFileDataAsRegularArray(node);
node.usedBytes = node.contents.length; // We might be writing to a lazy-loaded file which had overridden this property, so force-reset it.
}
#endif
if (!node.contents || node.contents.subarray) { // Keep using a typed array if creating a new storage, or if old one was a typed array as well.
var prevCapacity = node.contents ? node.contents.buffer.byteLength : 0;
if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.
// Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.
// For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to
// avoid overshooting the allocation cap by a very large margin.
var CAPACITY_DOUBLING_MAX = 1024 * 1024;
newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) | 0);
if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.
var oldContents = node.contents;
node.contents = new Uint8Array(newCapacity); // Allocate new storage.
if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.
return;
}
#endif
// Not using a typed array to back the file storage. Use a standard JS array instead.
if (!node.contents && newCapacity > 0) node.contents = [];
while (node.contents.length < newCapacity) node.contents.push(0);
},
// Performs an exact resize of the backing file storage to the given size, if the size is not exactly this, the storage is fully reallocated.
resizeFileStorage: function(node, newSize) {
if (node.usedBytes == newSize) return;
if (newSize == 0) {
node.contents = null; // Fully decommit when requesting a resize to zero.
node.usedBytes = 0;
return;
}
#if USE_TYPED_ARRAYS == 2
if (!node.contents || node.contents.subarray) { // Resize a typed array if that is being used as the backing store.
var oldContents = node.contents;
node.contents = new Uint8Array(new ArrayBuffer(newSize)); // Allocate new storage.
node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.
node.usedBytes = newSize;
return;
}
#endif
// Backing with a JS array.
if (!node.contents) node.contents = [];
if (node.contents.length > newSize) node.contents.length = newSize;
else while (node.contents.length < newSize) node.contents.push(0);
node.usedBytes = newSize;
},
node_ops: {
getattr: function(node) {
var attr = {};
@ -108,7 +181,7 @@ mergeInto(LibraryManager.library, {
if (FS.isDir(node.mode)) {
attr.size = 4096;
} else if (FS.isFile(node.mode)) {
attr.size = node.contents.length;
attr.size = node.usedBytes;
} else if (FS.isLink(node.mode)) {
attr.size = node.link.length;
} else {
@ -131,10 +204,7 @@ mergeInto(LibraryManager.library, {
node.timestamp = attr.timestamp;
}
if (attr.size !== undefined) {
MEMFS.ensureFlexible(node);
var contents = node.contents;
if (attr.size < contents.length) contents.length = attr.size;
else while (attr.size > contents.length) contents.push(0);
MEMFS.resizeFileStorage(node, attr.size);
}
},
lookup: function(parent, name) {
@ -198,9 +268,8 @@ mergeInto(LibraryManager.library, {
stream_ops: {
read: function(stream, buffer, offset, length, position) {
var contents = stream.node.contents;
if (position >= contents.length)
return 0;
var size = Math.min(contents.length - position, length);
if (position >= stream.node.usedBytes) return 0;
var size = Math.min(stream.node.usedBytes - position, length);
assert(size >= 0);
#if USE_TYPED_ARRAYS == 2
if (size > 8 && contents.subarray) { // non-trivial, and typed array
@ -208,47 +277,56 @@ mergeInto(LibraryManager.library, {
} else
#endif
{
for (var i = 0; i < size; i++) {
buffer[offset + i] = contents[position + i];
}
for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];
}
return size;
},
// Writes the byte range (buffer[offset], buffer[offset+length]) to offset 'position' into the file pointed by 'stream'
write: function(stream, buffer, offset, length, position, canOwn) {
if (!length) return 0;
var node = stream.node;
node.timestamp = Date.now();
var contents = node.contents;
#if USE_TYPED_ARRAYS == 2
if (length && contents.length === 0 && position === 0 && buffer.subarray) {
// just replace it with the new data
if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?
if (canOwn) { // Can we just reuse the buffer we are given?
#if ASSERTIONS
assert(buffer.length);
assert(position === 0, 'canOwn must imply no weird position inside the file');
#endif
if (canOwn && offset === 0) {
node.contents = buffer; // this could be a subarray of Emscripten HEAP, or allocated from some other source.
node.contentMode = (buffer.buffer === HEAP8.buffer) ? MEMFS.CONTENT_OWNING : MEMFS.CONTENT_FIXED;
} else {
node.contents = new Uint8Array(buffer.subarray(offset, offset+length));
node.contentMode = MEMFS.CONTENT_FIXED;
node.contents = buffer.subarray(offset, offset + length);
node.usedBytes = length;
return length;
} else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.
node.contents = new Uint8Array(buffer.subarray(offset, offset + length));
node.usedBytes = length;
return length;
} else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?
node.contents.set(buffer.subarray(offset, offset + length), position);
return length;
}
return length;
}
#endif
MEMFS.ensureFlexible(node);
var contents = node.contents;
while (contents.length < position) contents.push(0);
for (var i = 0; i < length; i++) {
contents[position + i] = buffer[offset + i];
}
// Appending to an existing file and we need to reallocate, or source data did not come as a typed array.
MEMFS.expandFileStorage(node, position+length);
#if USE_TYPED_ARRAYS == 2
if (node.contents.subarray && buffer.subarray) node.contents.set(buffer.subarray(offset, offset + length), position); // Use typed array write if available.
else
#endif
for (var i = 0; i < length; i++) {
node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not.
}
node.usedBytes = Math.max(node.usedBytes, position+length);
return length;
},
llseek: function(stream, offset, whence) {
var position = offset;
if (whence === 1) { // SEEK_CUR.
position += stream.position;
} else if (whence === 2) { // SEEK_END.
if (FS.isFile(stream.node.mode)) {
position += stream.node.contents.length;
position += stream.node.usedBytes;
}
}
if (position < 0) {
@ -259,10 +337,8 @@ mergeInto(LibraryManager.library, {
return position;
},
allocate: function(stream, offset, length) {
MEMFS.ensureFlexible(stream.node);
var contents = stream.node.contents;
var limit = offset + length;
while (limit > contents.length) contents.push(0);
MEMFS.expandFileStorage(stream.node, offset + length);
stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);
},
mmap: function(stream, buffer, offset, length, position, prot, flags) {
if (!FS.isFile(stream.node.mode)) {
@ -280,7 +356,7 @@ mergeInto(LibraryManager.library, {
ptr = contents.byteOffset;
} else {
// Try to avoid unnecessary slices.
if (position > 0 || position + length < contents.length) {
if (position > 0 || position + length < stream.node.usedBytes) {
if (contents.subarray) {
contents = contents.subarray(position, position + length);
} else {

Просмотреть файл

@ -84,6 +84,9 @@ var LibrarySDL = {
TOUCH_DEFAULT_ID: 0, // Our default deviceID for touch events (we get nothing from the browser)
eventHandler: null,
eventHandlerContext: null,
keyCodes: { // DOM code ==> SDL code. See https://developer.mozilla.org/en/Document_Object_Model_%28DOM%29/KeyboardEvent and SDL_keycode.h
// For keys that don't have unicode value, we map DOM codes with the corresponding scan codes + 1024 (using "| 1 << 10")
16: 225 | 1<<10, // shift
@ -304,7 +307,12 @@ var LibrarySDL = {
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pitch, 'width * bpp', 'i32') }}}; // assuming RGBA or indexed for now,
// since that is what ImageData gives us in browsers
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.pixels, 'buffer', 'void*') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect, '0', 'i32*') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.x, '0', 'i32') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.y, '0', 'i32') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.w, 'Module["canvas"].width', 'i32') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.clip_rect+C_STRUCTS.SDL_Rect.h, 'Module["canvas"].height', 'i32') }}};
{{{ makeSetValue('surf', C_STRUCTS.SDL_Surface.refcount, '1', 'i32') }}};
{{{ makeSetValue('pixelFormat', C_STRUCTS.SDL_PixelFormat.format, cDefine('SDL_PIXELFORMAT_RGBA8888'), 'i32') }}};
@ -699,6 +707,9 @@ var LibrarySDL = {
Module.printErr('SDL event queue full, dropping events');
SDL.events = SDL.events.slice(0, 10000);
}
// If we have a handler installed, this will push the events to the app
// instead of the app polling for them.
SDL.flushEventsToHandler();
return;
},
@ -750,6 +761,30 @@ var LibrarySDL = {
}
},
flushEventsToHandler: function() {
if (!SDL.eventHandler) return;
// All SDLEvents take the same amount of memory
var sdlEventPtr = allocate({{{ C_STRUCTS.SDL_KeyboardEvent.__size__ }}}, "i8", ALLOC_STACK);
while (SDL.pollEvent(sdlEventPtr)) {
Runtime.dynCall('iii', SDL.eventHandler, [SDL.eventHandlerContext, sdlEventPtr]);
}
},
pollEvent: function(ptr) {
if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
// If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
// to automatically query for events, query for joystick events.
SDL.queryJoysticks();
}
if (SDL.events.length === 0) return 0;
if (ptr) {
SDL.makeCEvent(SDL.events.shift(), ptr);
}
return 1;
},
makeCEvent: function(event, ptr) {
if (typeof event === 'number') {
// This is a pointer to a native C event that was SDL_PushEvent'ed
@ -1715,16 +1750,7 @@ var LibrarySDL = {
},
SDL_PollEvent: function(ptr) {
if (SDL.initFlags & 0x200 && SDL.joystickEventState) {
// If SDL_INIT_JOYSTICK was supplied AND the joystick system is configured
// to automatically query for events, query for joystick events.
SDL.queryJoysticks();
}
if (SDL.events.length === 0) return 0;
if (ptr) {
SDL.makeCEvent(SDL.events.shift(), ptr);
}
return 1;
return SDL.pollEvent(ptr);
},
SDL_PushEvent: function(ptr) {
@ -1764,6 +1790,13 @@ var LibrarySDL = {
SDL.handleEvent(event);
});
},
// An Emscripten-specific extension to SDL: Some browser APIs require that they are called from within an event handler function.
// Allow recording a callback that will be called for each received event.
emscripten_SDL_SetEventHandler: function(handler, userdata) {
SDL.eventHandler = handler;
SDL.eventHandlerContext = userdata;
},
SDL_SetColors: function(surf, colors, firstColor, nColors) {
var surfData = SDL.surfaces[surf];
@ -2607,7 +2640,7 @@ var LibrarySDL = {
if (info && info.audio) {
info.audio.pause();
} else {
Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
//Module.printErr('Mix_Pause: no sound found for channel: ' + channel);
}
},

Просмотреть файл

@ -443,6 +443,31 @@ var LibraryManager = {
}
}
// apply synonyms. these are typically not speed-sensitive, and doing it this way makes it possible to not include hacks in the compiler
// (and makes it simpler to switch between SDL verisons, fastcomp and non-fastcomp, etc.).
var lib = LibraryManager.library;
libloop: for (var x in lib) {
if (x.lastIndexOf('__') > 0) continue; // ignore __deps, __*
if (lib[x + '__asm']) continue; // ignore asm library functions, those need to be fully optimized
if (typeof lib[x] === 'string') {
var target = x;
while (typeof lib[target] === 'string') {
if (lib[target].indexOf('(') >= 0) continue libloop;
target = lib[target];
}
if (typeof lib[target] === 'undefined' || typeof lib[target] === 'function') {
if (target.indexOf('Math_') < 0) {
lib[x] = new Function('return _' + target + '.apply(null, arguments)');
if (!lib[x + '__deps']) lib[x + '__deps'] = [];
lib[x + '__deps'].push(target);
} else {
lib[x] = new Function('return ' + target + '.apply(null, arguments)');
}
continue;
}
}
}
/*
// export code for CallHandlers.h
printErr('============================');

Просмотреть файл

@ -323,6 +323,10 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r
// so that you can create a virtual file system with all of the required files.
var CASE_INSENSITIVE_FS = 0; // If set to nonzero, the provided virtual filesystem if treated case-insensitive, like
// Windows and OSX do. If set to 0, the VFS is case-sensitive, like on Linux.
var MEMFS_APPEND_TO_TYPED_ARRAYS = 0; // If set to nonzero, MEMFS will always utilize typed arrays as the backing store
// for appending data to files. The default behavior is to use typed arrays for files
// when the file size doesn't change after initial creation, and for files that do
// change size, use normal JS arrays instead.
var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss
// When enabled, 0-initialized globals are sorted to the end of the globals list,
@ -474,6 +478,9 @@ var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser
// very partial - it is hard to fake a whole browser! - so
// keep your expectations low for this to work.
var DETERMINISTIC = 0; // If 1, we force Date.now(), Math.random, etc. to return deterministic
// results. Good for comparing builds for debugging purposes (and nothing else)
var BENCHMARK = 0; // If 1, will just time how long main() takes to execute, and not
// print out anything at all whatsoever. This is useful for benchmarking.

Просмотреть файл

@ -615,6 +615,14 @@ extern DECLSPEC void SDLCALL SDL_DelEventWatch(SDL_EventFilter filter,
extern DECLSPEC void SDLCALL SDL_FilterEvents(SDL_EventFilter filter,
void *userdata);
/**
* An Emscripten-specific extension to SDL: Some browser APIs require that they are called from within an event handler function.
* Allow recording a callback that will be called for each received event. This is used in place of SDL_PollEvent.
* Your application will be called whenever there are events available.
*/
extern DECLSPEC void SDLCALL emscripten_SDL_SetEventHandler(SDL_EventFilter handler,
void *userdata);
/*@{*/
#define SDL_QUERY -1
#define SDL_IGNORE 0

Просмотреть файл

@ -14,6 +14,8 @@
extern "C" {
#endif
#include <stdio.h>
#if !__EMSCRIPTEN__
#include <SDL/SDL.h> /* for SDL_Delay in async_call */
#endif
@ -515,6 +517,24 @@ int emscripten_get_compiler_setting(const char *name);
*/
void emscripten_debugger();
/*
* Get preloaded image data and the size of the image.
*
* Returns pointer to loaded image or NULL.
* width/height of image are written to w/h if data is valid.
* Pointer should be free()'d
*/
char *emscripten_get_preloaded_image_data(const char *path, int *w, int *h);
/*
* Get preloaded image data from a c FILE *.
*
* Returns pointer to loaded image or NULL.
* width/height of image are written to w/h if data is valid.
* Pointer should be free()'d
*/
char *emscripten_get_preloaded_image_data_from_FILE(FILE *file, int *w, int *h);
/* ===================================== */
/* Internal APIs. Be careful with these. */

Просмотреть файл

@ -0,0 +1,2 @@
You may place local includes that you require here and they will
be found by the default compiler search path.

24
tests/fs/test_append.c Normal file
Просмотреть файл

@ -0,0 +1,24 @@
#include<assert.h>
#include<stdio.h>
int main (int argc, char *argv[])
{
FILE *fp;
int res;
long len;
fp = fopen("testappend", "wb+");
res = fwrite("1234567890", 10, 1, fp);
fclose(fp);
fp = fopen("testappend", "ab+");
res = fwrite("1234567890", 10, 1, fp);
fseek(fp, -7, SEEK_END);
len = ftell(fp);
assert(len == 13);
fclose(fp);
puts("success");
return 0;
}

Просмотреть файл

@ -376,6 +376,10 @@ void PullInfo(){
extension = "GL_EXT_framebuffer_object";
printf("'%s' extension is %s.\n", extension, glfwExtensionSupported(extension) ? "supported" : "not supported");
extension = "glBindBuffer";
void* proc_addr = glfwGetProcAddress(extension);
printf("'%s' extension proc address is %p.\n", extension, proc_addr);
printf("Sleeping 1 sec...\n");
glfwSleep(1);
printf("...Done.\n");

Просмотреть файл

@ -4,6 +4,7 @@
#include <assert.h>
#include <emscripten.h>
#include <unistd.h>
#include <stdlib.h>
int testImage(SDL_Surface* screen, const char* fileName) {
SDL_Surface *image = IMG_Load(fileName);
@ -18,7 +19,16 @@ int testImage(SDL_Surface* screen, const char* fileName) {
int result = image->w;
SDL_BlitSurface (image, NULL, screen, NULL);
int w, h;
char *data = emscripten_get_preloaded_image_data(fileName, &w, &h);
assert(data);
assert(w == image->w);
assert(h == image->h);
SDL_FreeSurface (image);
free(data);
return result;
}

Просмотреть файл

@ -5,55 +5,68 @@
int result = 1;
int SDLCALL EventHandler(void *userdata, SDL_Event *event) {
switch(event->type) {
case SDL_KEYDOWN:
break;
case SDL_KEYUP:
// don't handle the modifier key events
if (event->key.keysym.sym == SDLK_LCTRL ||
event->key.keysym.sym == SDLK_LSHIFT ||
event->key.keysym.sym == SDLK_LALT) {
return 0;
}
if ((event->key.keysym.mod & KMOD_LCTRL) || (event->key.keysym.mod & KMOD_RCTRL)) {
result *= 2;
}
if ((event->key.keysym.mod & KMOD_LSHIFT) || (event->key.keysym.mod & KMOD_RSHIFT)) {
result *= 3;
}
if ((event->key.keysym.mod & KMOD_LALT) || (event->key.keysym.mod & KMOD_RALT)) {
result *= 5;
}
switch (event->key.keysym.sym) {
case SDLK_RIGHT: printf("right\n"); result *= 7; break;
case SDLK_LEFT: printf("left\n"); result *= 11; break;
case SDLK_DOWN: printf("down\n"); result *= 13; break;
case SDLK_UP: printf("up\n"); result *= 17; break;
case SDLK_a: printf("a\n"); result *= 19; break;
default: {
if (event->key.keysym.scancode == SDL_SCANCODE_B) {
printf("b scancode\n"); result *= 23; break;
}
printf("unknown key: sym %d scancode %d\n", event->key.keysym.sym, event->key.keysym.scancode);
REPORT_RESULT();
emscripten_run_script("throw 'done'"); // comment this out to leave event handling active. Use the following to log DOM keys:
// addEventListener('keyup', function(event) { console.log(event->keyCode) }, true)
}
}
break;
default: /* Report an unhandled event */
printf("I don't know what this event is (type=%d)!\n", event->type);
}
return 0;
}
void one() {
#ifndef TEST_EMSCRIPTEN_SDL_SETEVENTHANDLER
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_KEYDOWN:
break;
case SDL_KEYUP:
// don't handle the modifier key events
if (event.key.keysym.sym == SDLK_LCTRL ||
event.key.keysym.sym == SDLK_LSHIFT ||
event.key.keysym.sym == SDLK_LALT) {
return;
}
if ((event.key.keysym.mod & KMOD_LCTRL) || (event.key.keysym.mod & KMOD_RCTRL)) {
result *= 2;
}
if ((event.key.keysym.mod & KMOD_LSHIFT) || (event.key.keysym.mod & KMOD_RSHIFT)) {
result *= 3;
}
if ((event.key.keysym.mod & KMOD_LALT) || (event.key.keysym.mod & KMOD_RALT)) {
result *= 5;
}
switch (event.key.keysym.sym) {
case SDLK_RIGHT: printf("right\n"); result *= 7; break;
case SDLK_LEFT: printf("left\n"); result *= 11; break;
case SDLK_DOWN: printf("down\n"); result *= 13; break;
case SDLK_UP: printf("up\n"); result *= 17; break;
case SDLK_a: printf("a\n"); result *= 19; break;
default: {
if (event.key.keysym.scancode == SDL_SCANCODE_B) {
printf("b scancode\n"); result *= 23; break;
}
printf("unknown key: sym %d scancode %d\n", event.key.keysym.sym, event.key.keysym.scancode);
REPORT_RESULT();
emscripten_run_script("throw 'done'"); // comment this out to leave event handling active. Use the following to log DOM keys:
// addEventListener('keyup', function(event) { console.log(event.keyCode) }, true)
}
}
break;
default: /* Report an unhandled event */
printf("I don't know what this event is!\n");
}
EventHandler(0, &event);
}
#endif
}
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
#ifdef TEST_EMSCRIPTEN_SDL_SETEVENTHANDLER
emscripten_SDL_SetEventHandler(EventHandler, 0);
#else
if (argc == 1337) one(); // keep it alive
#endif
emscripten_run_script("keydown(1250);keydown(38);keyup(38);keyup(1250);"); // alt, up
emscripten_run_script("keydown(1248);keydown(1249);keydown(40);keyup(40);keyup(1249);keyup(1248);"); // ctrl, shift, down
emscripten_run_script("keydown(37);keyup(37);"); // left
@ -62,8 +75,6 @@ int main(int argc, char **argv) {
emscripten_run_script("keydown(66);keyup(66);"); // b
emscripten_run_script("keydown(100);keyup(100);"); // trigger the end
if (argc == 1337) one(); // keep it alive
return 0;
}

1393
tests/sqlite/speedtest1.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -750,35 +750,36 @@ window.close = function() {
self.btest('sdl_canvas_alpha.c', reference='sdl_canvas_alpha.png', reference_slack=9)
def test_sdl_key(self):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Module.postRun = function() {
function doOne() {
Module._one();
for defines in [[], ['-DTEST_EMSCRIPTEN_SDL_SETEVENTHANDLER']]:
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Module.postRun = function() {
function doOne() {
Module._one();
setTimeout(doOne, 1000/60);
}
setTimeout(doOne, 1000/60);
}
setTimeout(doOne, 1000/60);
}
function keydown(c) {
var event = document.createEvent("KeyboardEvent");
event.initKeyEvent("keydown", true, true, window,
0, 0, 0, 0,
c, c);
document.dispatchEvent(event);
}
function keydown(c) {
var event = document.createEvent("KeyboardEvent");
event.initKeyEvent("keydown", true, true, window,
0, 0, 0, 0,
c, c);
document.dispatchEvent(event);
}
function keyup(c) {
var event = document.createEvent("KeyboardEvent");
event.initKeyEvent("keyup", true, true, window,
0, 0, 0, 0,
c, c);
document.dispatchEvent(event);
}
''')
open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read()))
function keyup(c) {
var event = document.createEvent("KeyboardEvent");
event.initKeyEvent("keyup", true, true, window,
0, 0, 0, 0,
c, c);
document.dispatchEvent(event);
}
''')
open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read()))
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate()
self.run_browser('page.html', '', '/report_result?223092870')
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate()
self.run_browser('page.html', '', '/report_result?223092870')
def test_sdl_key_proxy(self):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
@ -1098,9 +1099,10 @@ keydown(100);keyup(100); // trigger the end
shutil.move('test.html', 'third.html')
def test_fs_idbfs_sync(self):
secret = str(time.time())
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DFIRST', '-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
secret = str(time.time())
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=mode + ['-DFIRST', '-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=mode + ['-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']'''])
def test_sdl_pumpevents(self):
# key events should be detected using SDL_PumpEvents

Просмотреть файл

@ -4030,11 +4030,14 @@ def process(filename):
src = open(path_from_root('tests', 'files.cpp'), 'r').read()
mem_file = 'src.cpp.o.js.mem'
try_delete(mem_file)
self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'),
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
if self.emcc_args and '--memory-init-file' in self.emcc_args:
assert os.path.exists(mem_file)
orig_args = self.emcc_args
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
self.emcc_args = orig_args + mode
try_delete(mem_file)
self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'),
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
if self.emcc_args and '--memory-init-file' in self.emcc_args:
assert os.path.exists(mem_file)
def test_files_m(self):
# Test for Module.stdin etc.
@ -4077,7 +4080,10 @@ def process(filename):
test_path = path_from_root('tests', 'core', 'test_fwrite_0')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
orig_args = self.emcc_args if self.emcc_args else []
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
self.emcc_args = orig_args + mode
self.do_run_from_file(src, output)
def test_fgetc_ungetc(self):
src = open(path_from_root('tests', 'stdio', 'test_fgetc_ungetc.c'), 'r').read()
@ -4275,7 +4281,10 @@ def process(filename):
if self.emcc_args is None: return self.skip('requires libcxx')
test_path = path_from_root('tests', 'core', 'test_wprintf')
src, output = (test_path + s for s in ('.c', '.out'))
self.do_run_from_file(src, output)
orig_args = self.emcc_args
for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
self.emcc_args = orig_args + mode
self.do_run_from_file(src, output)
def test_direct_string_constant_usage(self):
if self.emcc_args is None: return self.skip('requires libcxx')
@ -4339,6 +4348,11 @@ def process(filename):
out = path_from_root('tests', 'fs', 'test_writeFile.out')
self.do_run_from_file(src, out)
def test_fs_append(self):
if self.emcc_args is None: return self.skip('requires emcc')
src = open(path_from_root('tests', 'fs', 'test_append.c'), 'r').read()
self.do_run(src, 'success', force_c=True)
def test_unistd_access(self):
self.clear()
if not self.is_emscripten_abi(): return self.skip('asmjs-unknown-emscripten needed for inline js')
@ -4643,6 +4657,39 @@ PORT: 3979
'''
self.do_run(src, 'value\narray_item1\narray_item2\narray_item3\n3\n3.00\n2.20\nJansson: Node with ID `0` not found. Context has `10` nodes.\n0\nJansson: No JSON context.\njansson!')
def test_js_libraries(self):
if self.emcc_args == None: return self.skip('needs emcc')
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
#include <stdio.h>
extern "C" {
extern void printey();
extern int calcey(int x, int y);
}
int main() {
printey();
printf("*%d*\\n", calcey(10, 22));
return 0;
}
''')
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
mergeInto(LibraryManager.library, {
printey: function() {
Module.print('hello from lib!');
}
});
''')
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
mergeInto(LibraryManager.library, {
calcey: function(x, y) {
return x + y;
}
});
''')
self.emcc_args += ['--js-library', os.path.join(self.get_dir(), 'mylib1.js'), '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]
self.do_run(open(os.path.join(self.get_dir(), 'main.cpp'), 'r').read(), 'hello from lib!\n*32*\n')
def test_constglobalunion(self):
if self.emcc_args is None: return self.skip('needs emcc')
self.emcc_args += ['-s', 'EXPORT_ALL=1']
@ -5117,7 +5164,7 @@ def process(filename):
\'\'\'
FS.createDataFile('/', 'paper.pdf', eval(Module.read('paper.pdf.js')), true, false);
Module.callMain(Module.arguments);
Module.print("Data: " + JSON.stringify(FS.root.contents['filename-1.ppm'].contents.map(function(x) { return unSign(x, 8) })));
Module.print("Data: " + JSON.stringify(MEMFS.getFileDataAsRegularArray(FS.root.contents['filename-1.ppm']).map(function(x) { return unSign(x, 8) })));
\'\'\'
)
src.close()
@ -5167,7 +5214,7 @@ def process(filename):
))
).replace(
'// {{POST_RUN_ADDITIONS}}',
"Module.print('Data: ' + JSON.stringify(FS.analyzePath('image.raw').object.contents));"
"Module.print('Data: ' + JSON.stringify(MEMFS.getFileDataAsRegularArray(FS.analyzePath('image.raw').object)));"
)
open(filename, 'w').write(src)
'''
@ -6386,10 +6433,15 @@ def process(filename):
if (i < 10) throw i; // line 5
}
#include <iostream>
#include <string>
int main() {
std::string x = "ok"; // add libc++ stuff to make this big, test for #2410
int i;
scanf("%d", &i);
foo(i);
std::cout << x << std::endl;
return 0;
}
'''

Просмотреть файл

@ -1306,38 +1306,6 @@ This pointer might make sense in another type signature: i: 0
self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE))
def test_js_libraries(self):
open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write('''
#include <stdio.h>
extern "C" {
extern void printey();
extern int calcey(int x, int y);
}
int main() {
printey();
printf("*%d*\\n", calcey(10, 22));
return 0;
}
''')
open(os.path.join(self.get_dir(), 'mylib1.js'), 'w').write('''
mergeInto(LibraryManager.library, {
printey: function() {
Module.print('hello from lib!');
}
});
''')
open(os.path.join(self.get_dir(), 'mylib2.js'), 'w').write('''
mergeInto(LibraryManager.library, {
calcey: function(x, y) {
return x + y;
}
});
''')
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'),
'--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate()
self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js')))
def test_identical_basenames(self):
# Issue 287: files in different dirs but with the same basename get confused as the same,
# causing multiply defined symbol errors
@ -2901,4 +2869,26 @@ int main(int argc, char **argv) {
assert 'eval.' not in src
assert 'new Function' not in src
def test_init_file_at_offset(self):
open('src.cpp', 'w').write(r'''
#include <stdio.h>
int main() {
int data = 0x12345678;
FILE *f = fopen("test.dat", "wb");
fseek(f, 100, SEEK_CUR);
fwrite(&data, 4, 1, f);
fclose(f);
int data2;
f = fopen("test.dat", "rb");
fread(&data2, 4, 1, f); // should read 0s, not that int we wrote at an offset
printf("read: %d\n", data2);
fseek(f, 0, SEEK_END);
int size = ftell(f); // should be 104, not 4
fclose(f);
printf("file size is %d\n", size);
}
''')
Popen([PYTHON, EMCC, 'src.cpp']).communicate()
self.assertContained('read: 0\nfile size is 104\n', run_js('a.out.js'))

Просмотреть файл

@ -1,7 +1,14 @@
#!/usr/bin/env python
import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess
import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess, re, base64, struct, imghdr
WINDOWS = sys.platform == 'win32'
if WINDOWS:
import ctypes
stdout_handle = ctypes.windll.kernel32.GetStdHandle(-11)
LOG_FORMAT = 'short' # Either 'bare', 'short', or 'long'
ADB = 'adb' # Path to the adb executable
LOG_VERBOSE = False # Verbose printing enabled with --verbose
HOST = 'localhost' # The remote host to connect to the B2G device
PORT = 6000 # The port on the host on which the B2G device listens on
@ -18,7 +25,13 @@ def sizeof_fmt(num):
return "%3.1f%s" % (num, 'TB')
def zipdir(path, zipfilename):
zipf = zipfile.ZipFile(zipfilename, 'w')
try:
import zlib
zip_mode = zipfile.ZIP_DEFLATED
except:
zip_mode = zipfile.ZIP_STORED
zipf = zipfile.ZipFile(zipfilename, 'w', zip_mode)
files_to_compress = []
for root, dirs, files in os.walk(path):
for file in files:
@ -29,9 +42,10 @@ def zipdir(path, zipfilename):
(root, file) = tuple
filename = os.path.join(root, file)
filesize = os.path.getsize(filename)
print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + os.path.relpath(filename, path) + '" (' + sizeof_fmt(filesize) + ')...'
path_in_archive = os.path.relpath(filename, path)
print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + path_in_archive + '" (' + sizeof_fmt(filesize) + ')...'
n += 1
zipf.write(os.path.join(root, file))
zipf.write(os.path.join(root, file), path_in_archive)
zipf.close()
print 'Done. '
@ -46,30 +60,50 @@ def format_html(msg):
# Prints a verbose log message to stdout channel. Only shown if run with --verbose.
def logv(msg):
if LOG_VERBOSE:
sys.stdout.write(format_html(msg))
sys.stdout.write(format_html(msg) + '\n')
sys.stdout.flush()
# Reads data from the socket, and tries to parse what we have got so far as a JSON message.
# The messages are of form "bytelength:{jsondict}", where bytelength tells how many bytes
# there are in the data that comes after the colon.
# Returns a JSON dictionary of the received message.
def read_b2g_response():
def read_b2g_response(print_errors_to_console = True):
global read_queue, b2g_socket
read_queue += b2g_socket.recv(65536*2)
while ':' in read_queue:
semicolon = read_queue.index(':')
payload_len = int(read_queue[:semicolon])
payload = ''
while True:
semicolon = float('Inf')
payload_len = float('Inf')
try:
semicolon = read_queue.index(':')
payload_len = int(read_queue[:semicolon])
except:
pass
if semicolon+1+payload_len > len(read_queue):
read_queue += b2g_socket.recv(65536*2)
try:
read_queue += b2g_socket.recv(4096)
except socket.timeout, e:
pass # We simulate blocking sockets with looping over reads that time out, since on Windows, the user cannot press Ctrl-C to break on blocking sockets.
except Exception, e:
if e[0] == 57: # Socket is not connected
print 'Error! Failed to receive data from the device: socket is not connected!'
sys.exit(1)
else:
print 'Got exception ' + str(e)
raise
continue
payload = read_queue[semicolon+1:semicolon+1+payload_len]
read_queue = read_queue[semicolon+1+payload_len:]
logv('Read a message of size ' + str(payload_len) + 'b from socket.')
payload = json.loads(payload)
# Log received errors immediately to console
if print_errors_to_console and 'error' in payload:
print >> sys.stderr, 'Received error "' + payload['error'] + '"! Reason: ' + payload['message']
else:
break
return payload
# Sends a command to the B2G device and waits for the response and returns it as a JSON dict.
def send_b2g_cmd(to, cmd, data = {}):
def send_b2g_cmd(to, cmd, data = {}, print_errors_to_console = True):
global b2g_socket
msg = { 'to': to, 'type': cmd}
msg = dict(msg.items() + data.items())
@ -78,7 +112,7 @@ def send_b2g_cmd(to, cmd, data = {}):
msg = str(len(msg))+':'+msg
logv('Sending cmd:' + cmd + ' to:' + to)
b2g_socket.sendall(msg)
return read_b2g_response()
return read_b2g_response(print_errors_to_console)
def escape_bytes(b):
return str(b)
@ -102,7 +136,16 @@ def send_b2g_data_chunk(to, data_blob):
i += 1
message = '{"to":"'+to+'","type":"chunk","chunk":"' + ''.join(byte_str) + '"}'
message = str(len(message)) + ':' + message
logv('{"to":"'+to+'","type":"chunk","chunk":"<data>"}')
b2g_socket.sendall(message)
return read_b2g_response()
def send_b2g_bulk_data(to, data_blob):
message = 'bulk ' + to + ' stream ' + str(len(data_blob)) + ':'
logv(message)
b2g_socket.sendall(message)
b2g_socket.sendall(data_blob)
# It seems that B2G doesn't send any response JSON back after a bulk transfer is finished, so no read_b2g_response() here.
# Queries the device for a list of all installed apps.
def b2g_get_appslist():
@ -130,8 +173,303 @@ def print_applist(applist, running_app_manifests, print_removable):
num_printed += 1
return num_printed
def adb_devices():
try:
devices = subprocess.check_output([ADB, 'devices'])
devices = devices.strip().split('\n')[1:]
devices = map(lambda x: x.strip().split('\t'), devices)
return devices
except Exception, e:
return []
def b2g_get_prefs_filename():
return subprocess.check_output([ADB, 'shell', 'echo', '-n', '/data/b2g/mozilla/*.default/prefs.js'])
def b2g_get_prefs_data():
return subprocess.check_output([ADB, 'shell', 'cat', '/data/b2g/mozilla/*.default/prefs.js'])
def b2g_get_pref(sub):
prefs_data = b2g_get_prefs_data().split('\n')
# Filter to find all prefs that have the substring 'sub' in them.
r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)')
for line in prefs_data:
m = r.match(line)
if m and (sub is None or sub in m.group(1)):
print m.group(1) + ': ' + m.group(2).strip()
def b2g_set_pref(pref, value):
prefs_data = b2g_get_prefs_data().split('\n')
# Remove any old value of this pref.
r = re.compile('user_pref\w*\(\w*"([^"]*)"\w*,\w*([^\)]*)')
new_prefs_data = []
for line in prefs_data:
m = r.match(line)
if not m or m.group(1) != pref:
new_prefs_data += [line]
if value != None:
print 'Setting pref "' + pref + '" = ' + value
new_prefs_data += ['user_pref("' + pref + '", ' + value + ');']
else:
print 'Unsetting pref "' + pref + '"'
(oshandle, tempfilename) = tempfile.mkstemp(suffix='.js', prefix='ffdb_temp_')
os.write(oshandle, '\n'.join(new_prefs_data));
# Write the new pref
subprocess.check_output([ADB, 'shell', 'stop', 'b2g'])
subprocess.check_output([ADB, 'push', tempfilename, b2g_get_prefs_filename()])
subprocess.check_output([ADB, 'shell', 'start', 'b2g'])
print 'Rebooting phone...'
def delete_temp_file():
os.remove(tempfilename)
atexit.register(delete_temp_file)
def get_packaged_app_manifest(target_app_path):
if os.path.isdir(target_app_path):
manifest_file = os.path.join(target_app_path, 'manifest.webapp')
if not os.path.isfile(manifest_file):
print "Error: Failed to find FFOS packaged app manifest file '" + manifest_file + "'! That directory does not contain a packaged app?"
sys.exit(1)
return json.loads(open(manifest_file, 'r').read())
elif target_app_path.endswith('.zip') and os.path.isfile(target_app_path):
try:
z = zipfile.ZipFile(target_app_path, "r")
bytes = z.read('manifest.webapp')
except Exception, e:
print "Error: Failed to read FFOS packaged app manifest file 'manifest.webapp' in zip file '" + target_app_path + "'! Error: " + str(e)
sys.exit(1)
return None
return json.loads(str(bytes))
else:
print "Error: Path '" + target_app_path + "' is neither a directory or a .zip file to represent the location of a FFOS packaged app!"
sys.exit(1)
return None
def b2g_install(target_app_path):
if os.path.isdir(target_app_path):
print 'Zipping up the contents of directory "' + target_app_path + '"...'
(oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_')
zipdir(target_app_path, tempzip)
target_app_path = tempzip
# Remember to delete the temporary package after we quit.
def delete_temp_file():
os.remove(tempzip)
atexit.register(delete_temp_file)
print 'Uploading application package "' + target_app_path + '"...'
print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.'
app_file = open(target_app_path, 'rb')
data = app_file.read()
file_size = len(data)
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage', { 'bulk': 'true'}, print_errors_to_console = False) # This may fail if on old device.
start_time = time.time()
if 'actor' in uploadResponse and 'BulkActor' in uploadResponse['actor']: # New B2G 2.0 hotness: binary data transfer
packageUploadActor = uploadResponse['actor']
send_b2g_bulk_data(packageUploadActor, data)
else: # Old B2G 1.4 and older, serialize binary data in JSON text strings (SLOW!)
print 'Bulk upload is not supported, uploading binary data with old slow format. Consider flashing your device to FFOS 2.0 or newer to enjoy faster upload speeds.'
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage')
packageUploadActor = uploadResponse['actor']
chunk_size = 4*1024*1024
i = 0
while i < file_size:
chunk = data[i:i+chunk_size]
send_b2g_data_chunk(packageUploadActor, chunk)
i += chunk_size
bytes_uploaded = min(i, file_size)
cur_time = time.time()
secs_elapsed = cur_time - start_time
percentage_done = bytes_uploaded * 1.0 / file_size
total_time = secs_elapsed / percentage_done
time_left = total_time - secs_elapsed
print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed)
app_local_id = str(uuid.uuid4())
reply = send_b2g_cmd(webappsActorName, 'install', { 'appId': app_local_id, 'upload': packageUploadActor })
cur_time = time.time()
secs_elapsed = cur_time - start_time
print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed)
if not 'appId' in reply:
print 'Error: Application install failed! ' + str(reply)
sys.exit()
return reply['appId']
def b2g_app_command(app_command, app_name, print_errors_to_console = True):
apps = b2g_get_appslist()
for app in apps:
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
send_b2g_cmd(webappsActorName, app_command, { 'manifestURL': app['manifestURL'] })
return 0
if print_errors_to_console:
print 'Error! Application "' + app_name + '" was not found! Use the \'list\' command to find installed applications.'
return 1
def b2g_memory(app_name):
apps = b2g_get_appslist()
appActor = ''
for app in apps:
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
break
if 'actor' in appActor:
memoryActor = appActor['actor']['memoryActor']
measure = send_b2g_cmd(memoryActor, 'measure')
for k,v in measure.items():
if k != 'from':
if k in ['otherSize', 'jsStringsSize', 'jsObjectsSize', 'styleSize', 'jsOtherSize', 'domSize', 'total']: # These are formatted in bytes
print k + ': ' + sizeof_fmt(v)
else:
print k + ': ' + str(v)
def b2g_log(app_name, clear=False):
global LOG_FORMAT
apps = b2g_get_appslist()
appActor = ''
for app in apps:
if str(app['localId']) == app_name or app['name'] == app_name or app['manifestURL'] == app_name or app['id'] == app_name:
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
break
if 'actor' in appActor:
consoleActor = appActor['actor']['consoleActor']
if clear:
send_b2g_cmd(consoleActor, 'clearMessagesCache')
print 'Cleared message log.'
return 0
msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] })
if WINDOWS:
WARNING = 14 # FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
FAIL = 12 # FOREGROUND_RED | FOREGROUND_INTENSITY
INFO = 7 # FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
ENDC = ''
BOLD = ''
else:
WARNING = '\033[93m'
FAIL = '\033[91m'
INFO = ENDC = '\033[0m'
BOLD = "\033[1m"
def set_color(string, color):
if WINDOWS:
ctypes.windll.kernel32.SetConsoleTextAttribute(stdout_handle, color)
return string
else:
return color + string + ENDC
def reset_color():
if WINDOWS:
ctypes.windll.kernel32.SetConsoleTextAttribute(stdout_handle, INFO)
def log_b2g_message(msg):
msgs = []
if 'type' in msg and msg['type'] == 'consoleAPICall':
msgs = [msg['message']]
elif 'messages' in msg:
msgs = msg['messages']
for m in msgs:
args = m['arguments']
for arg in args:
if LOG_FORMAT == 'long':
text = str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg)
elif LOG_FORMAT == 'bare':
text = str(arg)
else: # Default to 'short'
text = str(m['functionName']) + '@' + os.path.basename(str(m['filename'])) + ':' + str(m['lineNumber']) + ': ' + str(arg)
if m['level'] == 'log':
color = INFO
channel = 'I/'
elif m['level'] == 'warn':
color = WARNING
channel = 'W/'
elif m['level'] == 'error':
color = FAIL
channel = 'E/'
else:
color = INFO
channel = m['level'] + '/'
text = set_color(channel + text, color)
print text
reset_color()
msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] })
log_b2g_message(msgs)
while True:
msg = read_b2g_response()
log_b2g_message(msg)
else:
print 'Application "' + sys.argv[2] + '" is not running!'
def b2g_screenshot(filename):
global deviceActorName
data_reply = send_b2g_cmd(deviceActorName, 'screenshotToDataURL')
data = data_reply['value']
if not isinstance(data, basestring): # The device is sending the screenshot in multiple fragments since it's too long to fit in one message?
data_get_actor = data['actor']
data_len = int(data['length'])
data = data['initial']
chunk_size = 65000
pos = len(data)
# Pull and assemble individual screenshot fragments.
while pos < data_len:
bytes_to_read = min(data_len - pos, chunk_size)
data_reply = send_b2g_cmd(data_get_actor, 'substring', { 'start': str(pos), 'end': str(pos + bytes_to_read) })
if len(data_reply['substring']) != bytes_to_read:
print >> sys.stderr, 'Error! Expected to receive ' + str(bytes_to_read) + ' bytes of image data, but got ' + str(len(data_reply['substring'])) + ' bytes instead!'
sys.exit(1)
data += data_reply['substring']
pos += bytes_to_read
send_b2g_cmd(data_get_actor, 'release') # We need to explicitly free the screenshot image string from the device, or the Devtools connection leaks resources!
# Expected format is "data:image/png;base64,<base64data>"
delim = re.search(",", data).start()
data_format = data[:delim]
if data_format != "data:image/png;base64":
print >> sys.stderr, "Error: Received screenshot from device in an unexpected format '" + data_format + "'!"
sys.exit(1)
data = data[delim+1:]
binary_data = base64.b64decode(data)
open(filename, 'wb').write(binary_data)
def get_png_image_size(filename):
fhandle = open(filename, 'rb')
head = fhandle.read(24)
if len(head) != 24:
return (-1, -1)
check = struct.unpack('>i', head[4:8])[0]
if check != 0x0d0a1a0a:
return (-1, -1)
return struct.unpack('>ii', head[16:24])
width, height = get_png_image_size(filename)
if width <= 0 or height <= 0:
print >> sys.stderr, "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "', but the contents may be corrupted!"
else:
print "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "' (" + str(width) + 'x' + str(height) + ' pixels).'
def b2g_get_description(desc):
global deviceActorName
data_reply = send_b2g_cmd(deviceActorName, 'getDescription')
# First try an exact match to requested desc
if desc and desc in data_reply['value']:
print desc + ': ' + str(data_reply['value'][desc])
else: # Print all with case-insensitive substring search
for k,v in data_reply['value'].items():
if not desc or desc.lower() in k.lower():
print k + ': ' + str(v)
def main():
global b2g_socket, webappsActorName
global b2g_socket, webappsActorName, deviceActorName, HOST, PORT, VERBOSE, ADB
if len(sys.argv) < 2 or '--help' in sys.argv or 'help' in sys.argv or '-v' in sys.argv:
print '''Firefox OS Debug Bridge, a tool for automating FFOS device tasks from the command line.
@ -140,30 +478,96 @@ def main():
list [--running] [--all]: Prints out the user applications installed on the device.
If --running is passed, only the currently opened apps are shown.
If --all is specified, then also uninstallable system applications are listed.
launch <app>: Starts the given application. If already running, brings to front.
launch <app> [--log]: Starts the given application. If already running, brings to front. If the --log option is passed, ffdb will
start persistently logging the execution of the given application.
close <app>: Terminates the execution of the given application.
uninstall <app>: Removes the given application from the device.
install <path>: Uploads and installs a packaged app that resides in the given local directory.
install <path> [--run] [--log]: Uploads and installs a packaged app that resides in the given local directory.
<path> may either refer to a directory containing a packaged app, or to a prepackaged zip file.
If the --run option is passed, the given application is immediately launched after the installation finishes.
If the --log option is passed, ffdb will start persistently logging the execution of the installed application.
log <app> [--clear]: Starts a persistent log listener that reads web console messages from the given application.
If --clear is passed, the message log for that application is cleared instead.
memory <app>: Dumps a memory usage summary for the given application.
navigate <url>: Opens the given web page in the B2G browser.
screenshot [filename.png]: Takes a screenshot of the current contents displayed on the device. If an optional
filename is specified, the screenshot is saved to that file. Otherwise the filename
will be autogenerated.
get [pref]: Fetches the value of the given developer pref option from the FFOS device and prints it to console. The parameter pref
is optional and may either be the full name of a pref, or a substring to search for. All matching prefs will be printed.
If no pref parameter is given, all prefs are printed.
NOTE: This function (currently at least) only reports prefs that have been explicitly set and don't have their default value.
set <pref> <value>: Writes the given pref option to the FFOS device and restarts the B2G process on it for the change to take effect.
unset <pref>: Removes the given pref option from the FFOS device and restarts the B2G process on it for the change to take effect.
hide-prompt: Permanently removes the remote debugging connection dialog from showing up, and reboots the phone. This command is
provided for conveniency, and is the same as calling './ffdb.py set devtools.debugger.prompt-connection false'
restore-prompt: Restores the remote debugging connection dialog prompt to its default state.
desc [desc]: Fetches the value of the given device description field. These fields are read-only and describe the current system.
If the optional desc parameter is omitted, all device descriptions are printed. Otherwise the given description is
printed if it is an exact match, or all descriptions containing desc as the substring are printed.
Options: Additionally, the following options may be passed to control FFDB execution:
--host <hostname>: Specifies the target network address to connect to. Default: 'localhost'.
--port <number>: Specifies the network port to connect to. Default: 6000.
--verbose: Enables verbose printing, mostly useful for debugging.
--simulator: Signal that we will be connecting to a FFOS simulator and not a real device.
In the above, whenever a command requires an <app> to be specified, either the human-readable name,
localId or manifestURL of the application can be used.'''
sys.exit(0)
connect_to_simulator = False
options_with_value = ['--host', '--port']
options = options_with_value + ['--verbose', '--simulator']
# Process options
for i in range(0, len(sys.argv)):
if sys.argv[i] in options_with_value:
if i+1 >= sys.argv or sys.argv[i+1].startswith('-'):
print >> sys.stderr, "Missing value for option " + sys.argv[i] +'!'
sys.exit(1)
if sys.argv[i] == '--host':
HOST = sys.argv[i+1]
elif sys.argv[i] == '--port':
PORT = int(sys.argv[i+1])
elif sys.argv[i] == '--verbose':
VERBOSE = True
elif sys.argv[i] == '--simulator':
connect_to_simulator = True
# Clear the processed options so that parsing the commands below won't trip up on these.
if sys.argv[i] in options: sys.argv[i] = ''
if sys.argv[i] in options_with_value: sys.argv[i+1] = ''
sys.argv = filter(lambda x: len(x) > 0, sys.argv)
# Double-check that the device is found via adb:
if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator:
devices = adb_devices()
if len(devices) == 0:
print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + ' and no devices were detected via adb. Please double-check the following and try again: '
print ' 1) The device is powered on and connected to the computer with an USB cable.'
print ' 2) ADB and DevTools debugging is enabled on the device. (Settings -> Developer -> Debugging via USB: "ADB and DevTools"'
print ' 3) The device is listed when you run "adb devices" on the command line.'
print ' 4) When launching ffdb, remember to acknowledge the "incoming debug connection" dialog if it pops up on the device.'
sys.exit(1)
b2g_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if WINDOWS:
# Python Windows issue: user cannot press Ctrl-C to abort from a socket .recv() Therefore simulate blocking sockets with looping over reads that time out.
b2g_socket.settimeout(0.5)
try:
b2g_socket.connect((HOST, PORT))
except Exception, e:
if e[0] == 61: # Connection refused
if HOST == 'localhost' or HOST == '127.0.0.1':
cmd = ['adb', 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket']
if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator:
cmd = [ADB, 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket']
print 'Connection to ' + HOST + ':' + str(PORT) + ' refused, attempting to forward device debugger-socket to local address by calling ' + str(cmd) + ':'
else:
print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + '!'
print 'Error! Failed to connect to B2G ' + ('simulator' if connect_to_simulator else 'device') + ' debugger socket at address ' + HOST + ':' + str(PORT) + '!'
sys.exit(1)
try:
retcode = subprocess.check_call(cmd)
@ -187,6 +591,9 @@ def main():
logv('Connected. Handshake: ' + str(handshake))
data = send_b2g_cmd('root', 'listTabs')
if not 'deviceActor' in data:
print 'Error! Debugging connection was not available. Make sure that the "Remote debugging" developer option on the device is set to "ADB and Devtools".'
sys.exit(1)
deviceActorName = data['deviceActor']
logv('deviceActor: ' + deviceActorName)
webappsActorName = data['webappsActor']
@ -195,9 +602,8 @@ def main():
send_b2g_cmd(deviceActorName, 'getDescription')
send_b2g_cmd(deviceActorName, 'getRawPermissionsTable')
apps = b2g_get_appslist()
if sys.argv[1] == 'list':
apps = b2g_get_appslist()
running_app_manifests = b2g_get_runningapps()
printed_apps = apps
print_only_running = '--running' in sys.argv and not '--all' in sys.argv
@ -218,54 +624,27 @@ def main():
if len(sys.argv) < 3:
print 'Error! No application name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <app>'
return 1
for app in apps:
if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]:
send_b2g_cmd(webappsActorName, sys.argv[1], { 'manifestURL': app['manifestURL'] })
return 0
print 'Error! Application "' + sys.argv[2] + '" was not found! Use the \'list\' command to find installed applications.'
return 1
ret = b2g_app_command(sys.argv[1], sys.argv[2])
if ret == 0 and '--log' in sys.argv:
b2g_log(sys.argv[2])
elif sys.argv[1] == 'install':
if len(sys.argv) < 3:
print 'Error! No application path given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <path>'
return 1
target_app_path = sys.argv[2]
if os.path.isdir(target_app_path):
print 'Zipping up the contents of directory "' + target_app_path + '"...'
(oshandle, tempzip) = tempfile.mkstemp(suffix='.zip', prefix='ffdb_temp_')
zipdir(target_app_path, tempzip)
target_app_path = tempzip
# Remember to delete the temporary package after we quit.
def delete_temp_file():
os.remove(tempzip)
atexit.register(delete_temp_file)
print 'Uploading application package "' + target_app_path + '"...'
print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.'
uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage')
packageUploadActor = uploadResponse['actor']
app_file = open(target_app_path, 'rb')
data = app_file.read()
file_size = len(data)
chunk_size = 4*1024*1024
i = 0
start_time = time.time()
while i < file_size:
chunk = data[i:i+chunk_size]
send_b2g_data_chunk(packageUploadActor, chunk)
i += chunk_size
bytes_uploaded = min(i, file_size)
cur_time = time.time()
secs_elapsed = cur_time - start_time
percentage_done = bytes_uploaded * 1.0 / file_size
total_time = secs_elapsed / percentage_done
time_left = total_time - secs_elapsed
print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed)
send_b2g_cmd(webappsActorName, 'install', { 'appId': str(uuid.uuid4()), 'upload': packageUploadActor })
cur_time = time.time()
secs_elapsed = cur_time - start_time
print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed)
# Kill and uninstall old running app execution before starting.
if '--run' in sys.argv:
app_manifest = get_packaged_app_manifest(target_app_path)
b2g_app_command('close', app_manifest['name'], print_errors_to_console=False)
b2g_app_command('uninstall', app_manifest['name'], print_errors_to_console=False)
# Upload package
app_id = b2g_install(target_app_path)
# Launch it immediately if requested.
if '--run' in sys.argv:
b2g_app_command('launch', app_id)
# Don't quit, but keep logging the app if requested.
if '--log' in sys.argv:
b2g_log(app_id)
elif sys.argv[1] == 'navigate':
if len(sys.argv) < 3:
print 'Error! No URL given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <url>'
@ -281,55 +660,44 @@ def main():
else:
print 'Web browser is not running!'
elif sys.argv[1] == 'log':
appActor = ''
for app in apps:
if str(app['localId']) == sys.argv[2] or app['name'] == sys.argv[2] or app['manifestURL'] == sys.argv[2]:
appActor = send_b2g_cmd(webappsActorName, 'getAppActor', { 'manifestURL': app['manifestURL'] })
break
if 'actor' in appActor:
consoleActor = appActor['actor']['consoleActor']
if '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv:
send_b2g_cmd(consoleActor, 'clearMessagesCache')
print 'Cleared message log.'
sys.exit(0)
msgs = send_b2g_cmd(consoleActor, 'startListeners', { 'listeners': ['PageError','ConsoleAPI','NetworkActivity','FileActivity'] })
def log_b2g_message(msg):
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = "\033[1m"
msgs = []
if 'type' in msg and msg['type'] == 'consoleAPICall':
msgs = [msg['message']]
elif 'messages' in msg:
msgs = msg['messages']
for m in msgs:
args = m['arguments']
for arg in args:
if m['level'] == 'log':
color = 'I/'
elif m['level'] == 'warn':
color = WARNING + 'W/'
elif m['level'] == 'error':
color = FAIL + 'E/'
else:
color = m['level'] + '/'
print color + str(m['functionName']) + '@' + str(m['filename']) + ':' + str(m['lineNumber']) + ': ' + str(arg) + ENDC
msgs = send_b2g_cmd(consoleActor, 'getCachedMessages', { 'messageTypes': ['PageError', 'ConsoleAPI'] })
log_b2g_message(msgs)
while True:
msg = read_b2g_response()
log_b2g_message(msg)
clear = '-c' in sys.argv or '-clear' in sys.argv or '--clear' in sys.argv
b2g_log(sys.argv[2], clear)
elif sys.argv[1] == 'memory':
b2g_memory(sys.argv[2])
elif sys.argv[1] == 'screenshot':
if len(sys.argv) >= 3:
filename = sys.argv[2]
if not filename.endswith('.png'):
print >> sys.stderr, "Writing screenshots only to .png files are supported!"
sys.exit(1)
else:
print 'Application "' + sys.argv[2] + '" is not running!'
filename = time.strftime("screen_%Y%m%d_%H%M%S.png", time.gmtime())
b2g_screenshot(filename)
elif sys.argv[1] == 'get':
b2g_get_pref(sys.argv[2] if len(sys.argv) >= 3 else None)
elif sys.argv[1] == 'set':
if len(sys.argv) < 3:
print 'Error! No pref name to set given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
sys.exit(1)
if len(sys.argv) < 4:
print 'Error! No value given to set! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
sys.exit(1)
if len(sys.argv) > 4:
print 'Error! Too many arguments given (' + str(sys.argv) + '), need exactly four! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref> <value>'
sys.exit(1)
b2g_set_pref(sys.argv[2], sys.argv[3])
elif sys.argv[1] == 'unset':
if len(sys.argv) < 3:
print 'Error! No pref name given! Usage: ' + sys.argv[0] + ' ' + sys.argv[1] + ' <pref>'
sys.exit(1)
b2g_set_pref(sys.argv[2], None)
elif sys.argv[1] == 'hide-prompt':
b2g_set_pref('devtools.debugger.prompt-connection', 'false')
elif sys.argv[1] == 'restore-prompt':
b2g_set_pref('devtools.debugger.prompt-connection', None)
elif sys.argv[1] == 'desc':
b2g_get_description(sys.argv[2] if len(sys.argv) >= 3 else None)
else:
print "Unknown command '" + sys.argv[1] + "'! Pass --help for instructions."
@ -337,6 +705,10 @@ def main():
return 0
if __name__ == '__main__':
returncode = main()
logv('ffdb.py quitting with process exit code ' + str(returncode))
sys.exit(returncode)
try:
returncode = main()
logv('ffdb.py quitting with process exit code ' + str(returncode))
sys.exit(returncode)
except KeyboardInterrupt:
print ('^C' if WINDOWS else '') + ' Aborted by user'
sys.exit(1)

Просмотреть файл

@ -128,10 +128,8 @@ load('utility.js');
// Utilities
var FUNCTION = set('defun', 'function');
var LOOP = set('do', 'while', 'for');
var LOOP_FLOW = set('break', 'continue');
var ASSIGN_OR_ALTER = set('assign', 'unary-postfix', 'unary-prefix');
var CONTROL_FLOW = set('do', 'while', 'for', 'if', 'switch');
var NAME_OR_NUM = set('name', 'num');
var ASSOCIATIVE_BINARIES = set('+', '*', '|', '&', '^');
@ -145,10 +143,7 @@ var COMMABLE = set('assign', 'binary', 'unary-prefix', 'unary-postfix', 'name',
var FUNCTIONS_THAT_ALWAYS_THROW = set('abort', '___resumeException', '___cxa_throw', '___cxa_rethrow');
var NULL_NODE = ['name', 'null'];
var UNDEFINED_NODE = ['unary-prefix', 'void', ['num', 0]];
var TRUE_NODE = ['unary-prefix', '!', ['num', 0]];
var FALSE_NODE = ['unary-prefix', '!', ['num', 1]];
var GENERATED_FUNCTIONS_MARKER = '// EMSCRIPTEN_GENERATED_FUNCTIONS';
var generatedFunctions = false; // whether we have received only generated functions

Просмотреть файл

@ -706,10 +706,8 @@ if USE_EMSDK:
path_from_root('system', 'include', 'compat'),
path_from_root('system', 'include'),
path_from_root('system', 'include', 'emscripten'),
path_from_root('system', 'include', 'bsd'), # posix stuff
path_from_root('system', 'include', 'libc'),
path_from_root('system', 'include', 'gfx'),
path_from_root('system', 'include', 'net'),
path_from_root('system', 'include', 'SDL'),
]
@ -1426,12 +1424,17 @@ class Building:
settings = Settings.serialize()
args = settings + extra_args
if WINDOWS:
args = ['@' + response_file.create_response_file(args, TEMP_DIR)]
rsp_file = response_file.create_response_file(args, TEMP_DIR)
args = ['@' + rsp_file]
cmdline = [PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + args
if jsrun.TRACK_PROCESS_SPAWNS:
logging.info('Executing emscripten.py compiler with cmdline "' + ' '.join(cmdline) + '"')
jsrun.timeout_run(Popen(cmdline, stdout=PIPE), None, 'Compiling')
# Clean up .rsp file the compiler used after we are finished.
if WINDOWS:
try_delete(rsp_file)
# Detect compilation crashes and errors
assert os.path.exists(filename + '.o.js'), 'Emscripten failed to generate .js'

Просмотреть файл

@ -427,12 +427,18 @@ def calculate(temp_files, in_temp, stdout, stderr):
# TODO: Move all __deps from src/library*.js to deps_info.json, and use that single source of info
# both here and in the JS compiler.
deps_info = json.loads(open(shared.path_from_root('src', 'deps_info.json')).read())
added = set()
def add_back_deps(need):
more = False
for ident, deps in deps_info.iteritems():
if ident in need.undefs:
if ident in need.undefs and not ident in added:
added.add(ident)
more = True
for dep in deps:
need.undefs.add(dep)
shared.Settings.EXPORTED_FUNCTIONS.append('_' + dep)
if more:
add_back_deps(need) # recurse to get deps of deps
for symbols in symbolses:
add_back_deps(symbols)