Skip to content

Source maps (Refs #1252, Reopens #1267) #1274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Jun 26, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
86e672a
Commit the source-map library.
int3 Jun 7, 2013
747f74e
Implement basic source maps. Closes #1252.
int3 Jun 7, 2013
165befa
Add check for keep_llvm_debug + minor touchups.
int3 Jun 7, 2013
2f16717
Add test for source maps.
int3 Jun 7, 2013
2c0dcd9
Ensure line numbers are the same in HTML.
int3 Jun 8, 2013
af52a9a
Fix source map line numbering for inline scripts.
int3 Jun 10, 2013
be5d45f
Add test for HTML source map generation.
int3 Jun 10, 2013
7656f94
Lay the groundwork for optimized source maps.
int3 Jun 19, 2013
0c3cb93
Delete uglify code that was only needed for compression.
int3 Jun 19, 2013
0f73e28
Map source lines for assignment statements.
int3 Jun 19, 2013
54a59be
Move source mapping files into their own folder.
int3 Jun 19, 2013
050335f
Update the source-map library.
int3 Jun 19, 2013
d680f5e
Implement source maps for optimized builds.
int3 Jun 19, 2013
486413c
Make sure the line numbers sync up.
int3 Jun 20, 2013
cfe4eb5
Source maps should only be activated via the `--map` flag.
int3 Jun 20, 2013
5459b35
Fix line numbering for invoke instructions.
int3 Jun 20, 2013
99fa57e
Make test_debug actually fail when things go wrong.
int3 Jun 20, 2013
7212198
Make optimizer handle both strings and string-like type objects.
int3 Jun 21, 2013
4eef4be
Put uglify back the way it was.
int3 Jun 21, 2013
d50e7b4
Improvements to test_html_source_map.
int3 Jun 21, 2013
88feddf
Get test_source_map passing again.
int3 Jun 21, 2013
9382556
Test that source mapping works with EMCC_DEBUG=2.
int3 Jun 22, 2013
6103884
Ensure JS output remains the same when making source maps.
int3 Jun 22, 2013
3a84846
Centralize debug options in a -gX flag.
int3 Jun 22, 2013
2b54c4f
Fix up the source map JS output 'reftest'.
int3 Jun 22, 2013
7ff0dde
Merge remote-tracking branch 'upstream/incoming' into source-maps
int3 Jun 23, 2013
5eaa7ba
Ensure original test environment is restored.
int3 Jun 23, 2013
452b871
Optimize!
int3 Jun 24, 2013
6768d77
Clean up emcc flag parsing + help message.
int3 Jun 24, 2013
0c19e40
Get rid of stale comment.
int3 Jun 25, 2013
5664470
Move line numbers to the AST node itself.
int3 Jun 26, 2013
e3a37fc
Strict compare all the things!
int3 Jun 26, 2013
e08502d
We can just compare for strings here.
int3 Jun 26, 2013
5383aa8
Reuse nodes where possible.
int3 Jun 26, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 57 additions & 20 deletions emcc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ emcc can be influenced by a few environment variables:

import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging
from subprocess import PIPE, STDOUT
from tools import shared
from tools import shared, jsrun
from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename
from tools.response_file import read_response_file

Expand Down Expand Up @@ -203,10 +203,18 @@ Options that are modified or new in %s include:
-g2 Preserve function names
-g3 Preserve variable names
-g4 Preserve LLVM debug info (if -g was
used when compiling the C/C++ sources)
and show line number debug comments.
This is the highest level of debuggability.
(default in -O0)
used when compiling the C/C++ sources),
show line number debug comments, and
generate source maps. This is the highest
level of debuggability. Note that this
may make -O1 and above significantly
slower because JS optimization will be
limited to 1 core. (default in -O0)

-g2 Like -g1, but we generate source maps as well,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emcc now defines -g0 to -g4. this should go in -g4 (highest level of debuggability)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, missed this out while doing the merge.

and we preserve comments even with -O1 and above.
Note that this may be considerably slower because
JS optimization is limited to a single core.

--typed-arrays <mode> 0: No typed arrays
1: Parallel typed arrays
Expand Down Expand Up @@ -731,6 +739,14 @@ try:

settings_changes = []

def validate_arg_level(level_string, max_level, err_msg):
try:
level = int(level_string)
assert 0 <= level <= max_level
except:
raise Exception(err_msg)
return level

for i in range(len(newargs)):
newargs[i] = newargs[i].strip() # On Windows Vista (and possibly others), excessive spaces in the command line leak into the items in this array, so trim e.g. 'foo.cpp ' -> 'foo.cpp'
if newargs[i].startswith('-O'):
Expand All @@ -739,11 +755,7 @@ try:
if requested_level == 's':
requested_level = 2
settings_changes.append('INLINING_LIMIT=50')
try:
opt_level = int(requested_level)
assert 0 <= opt_level <= 3
except:
raise Exception('Invalid optimization level: ' + newargs[i])
opt_level = validate_arg_level(requested_level, 3, 'Invalid optimization level: ' + newargs[i])
newargs[i] = ''
elif newargs[i].startswith('--llvm-opts'):
check_bad_eq(newargs[i])
Expand Down Expand Up @@ -787,12 +799,8 @@ try:
newargs[i+1] = ''
elif newargs[i].startswith('-g'):
requested_level = newargs[i][2:] or '3'
try:
debug_level = int(requested_level)
assert 0 <= debug_level <= 4
except:
raise Exception('Invalid debug level: ' + newargs[i])
newargs[i] = '-g' # discard level for clang args
debug_level = validate_arg_level(requested_level, 4, 'Invalid debug level: ' + newargs[i])
newargs[i] = '-g' # we'll need this to get LLVM debug info
elif newargs[i] == '--bind':
bind = True
newargs[i] = ''
Expand Down Expand Up @@ -884,6 +892,11 @@ try:
if opt_level == 0: debug_level = 4
if closure is None and opt_level == 3: closure = True

# TODO: support source maps with js_transform
if js_transform and debug_level >= 4:
logging.warning('disabling source maps because a js transform is being done')
debug_level = 3

if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state

if closure:
Expand Down Expand Up @@ -1408,6 +1421,7 @@ try:
# Optimize, if asked to
if not LEAVE_INPUTS_RAW:
link_opts = [] if debug_level >= 4 else ['-strip-debug'] # remove LLVM debug if we are not asked for it

if llvm_opts > 0:
if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'):
shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts)
Expand Down Expand Up @@ -1495,9 +1509,11 @@ try:
final += '.tr.js'
posix = True if not shared.WINDOWS else False
logging.debug('applying transform: %s' % js_transform)
execute(shlex.split(js_transform, posix=posix) + [os.path.abspath(final)])
subprocess.check_call(shlex.split(js_transform, posix=posix) + [os.path.abspath(final)])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures that we throw an error when the transform fails, allowing failures in post-build hooks to be recorded by the test runner.

if DEBUG: save_intermediate('transformed')

js_transform_tempfiles = [final]

# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
js_optimizer_queue = []
def flush_js_optimizer_queue():
Expand All @@ -1507,15 +1523,17 @@ try:
if shared.Settings.ASM_JS:
js_optimizer_queue = ['asm'] + js_optimizer_queue
logging.debug('applying js optimization passes: %s', js_optimizer_queue)
final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache)
final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4)
js_transform_tempfiles.append(final)
if DEBUG: save_intermediate('js_opts')
else:
for name in js_optimizer_queue:
passes = [name]
if shared.Settings.ASM_JS:
passes = ['asm'] + passes
logging.debug('applying js optimization pass: %s', passes)
final = shared.Building.js_optimizer(final, passes, jcache)
final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4)
js_transform_tempfiles.append(final)
save_intermediate(name)
js_optimizer_queue = []

Expand All @@ -1524,7 +1542,8 @@ try:

if DEBUG == '2':
# Clean up the syntax a bit
final = shared.Building.js_optimizer(final, [], jcache)
final = shared.Building.js_optimizer(final, [], jcache, debug_level >= 4)
js_transform_tempfiles.append(final)
if DEBUG: save_intermediate('pretty')

def get_eliminate():
Expand All @@ -1542,6 +1561,8 @@ try:
flush_js_optimizer_queue()

logging.debug('running closure')
# no need to add this to js_transform_tempfiles, because closure and
# debug_level > 0 are never simultaneously true
final = shared.Building.closure_compiler(final)
if DEBUG: save_intermediate('closure')

Expand Down Expand Up @@ -1589,19 +1610,33 @@ try:
src = re.sub('/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, Runtime\.GLOBAL_BASE\)', repl, src, count=1)
open(final + '.mem.js', 'w').write(src)
final += '.mem.js'
js_transform_tempfiles[-1] = final # simple text substitution preserves comment line number mappings
if DEBUG:
if os.path.exists(memfile):
save_intermediate('meminit')
logging.debug('wrote memory initialization to %s' % memfile)
else:
logging.debug('did not see memory initialization')

def generate_source_map(map_file_base_name, offset=0):
jsrun.run_js(shared.path_from_root('tools', 'source-maps', 'sourcemapper.js'),
shared.NODE_JS, js_transform_tempfiles +
['--sourceRoot', os.getcwd(),
'--mapFileBaseName', map_file_base_name,
'--offset', str(offset)])

# If we were asked to also generate HTML, do that
if final_suffix == 'html':
logging.debug('generating HTML')
shell = open(shell_path).read()
html = open(target, 'w')
if not Compression.on:
if debug_level >= 4:
match = re.match('.*?<script[^>]*>{{{ SCRIPT_CODE }}}</script>', shell,
re.DOTALL)
if match is None:
raise RuntimeError('Could not find script insertion point')
generate_source_map(target, match.group().count('\n'))
html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read()))
else:
# Compress the main code
Expand Down Expand Up @@ -1672,6 +1707,8 @@ try:
from tools.split import split_javascript_file
split_javascript_file(final, unsuffixed(target), split_js_file)
else:
if debug_level >= 4: generate_source_map(target)

# copy final JS to output
shutil.move(final, target)

Expand Down
4 changes: 3 additions & 1 deletion src/jsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,9 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
i++;
return JS + (Debugging.on ? Debugging.getComment(line.lineNum) : '');
// invoke instructions span two lines, and the debug info is located
// on the second line, hence the +1
return JS + (Debugging.on ? Debugging.getComment(line.lineNum + (line.intertype === 'invoke' ? 1 : 0)) : '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice hack ;)

})
.join('\n')
.split('\n') // some lines include line breaks
Expand Down
6 changes: 1 addition & 5 deletions src/shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@
};
Module.setStatus('Downloading...');
</script>
<script type='text/javascript'>

{{{ SCRIPT_CODE }}}

</script>
<script type='text/javascript'>{{{ SCRIPT_CODE }}}</script>
</body>
</html>
Loading