enable a form of safe heap in asm, using js optimizer pass to ensure full coverage and support for fastcomp
This commit is contained in:
Родитель
e14e6ea75c
Коммит
a310827131
6
emcc
6
emcc
|
@ -1196,7 +1196,6 @@ try:
|
||||||
shared.Settings.ASM_JS = 1
|
shared.Settings.ASM_JS = 1
|
||||||
assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet'
|
assert shared.Settings.ALLOW_MEMORY_GROWTH == 0, 'memory growth not supported in fastcomp yet'
|
||||||
assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp'
|
assert shared.Settings.UNALIGNED_MEMORY == 0, 'forced unaligned memory not supported in fastcomp'
|
||||||
assert shared.Settings.SAFE_HEAP == 0, 'safe heap not supported in fastcomp yet'
|
|
||||||
assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet'
|
assert shared.Settings.CHECK_HEAP_ALIGN == 0, 'check heap align not supported in fastcomp yet'
|
||||||
assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'
|
assert shared.Settings.SAFE_DYNCALLS == 0, 'safe dyncalls not supported in fastcomp'
|
||||||
assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp'
|
assert shared.Settings.RESERVED_FUNCTION_POINTERS == 0, 'reserved function pointers not supported in fastcomp'
|
||||||
|
@ -1230,6 +1229,9 @@ try:
|
||||||
shared.Settings.CORRECT_OVERFLOWS = 1
|
shared.Settings.CORRECT_OVERFLOWS = 1
|
||||||
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
|
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
|
||||||
|
|
||||||
|
if shared.Settings.SAFE_HEAP and not js_opts:
|
||||||
|
logging.warning('asm.js+SAFE_HEAP requires js opts to be run (-O1 or above by default)')
|
||||||
|
|
||||||
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
|
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
|
||||||
debug_level = 4 # must keep debug info to do line-by-line operations
|
debug_level = 4 # must keep debug info to do line-by-line operations
|
||||||
|
|
||||||
|
@ -1991,6 +1993,8 @@ try:
|
||||||
if DEBUG: save_intermediate('closure')
|
if DEBUG: save_intermediate('closure')
|
||||||
|
|
||||||
if js_opts:
|
if js_opts:
|
||||||
|
if shared.Settings.ASM_JS and shared.Settings.SAFE_HEAP: js_optimizer_queue += ['safeHeap']
|
||||||
|
|
||||||
if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS:
|
if shared.Settings.OUTLINING_LIMIT > 0 and shared.Settings.ASM_JS:
|
||||||
js_optimizer_queue += ['outline']
|
js_optimizer_queue += ['outline']
|
||||||
js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT
|
js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT
|
||||||
|
|
|
@ -455,7 +455,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
|
||||||
|
|
||||||
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
|
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
|
||||||
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
|
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
|
||||||
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
|
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE']
|
||||||
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
|
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
|
||||||
if settings['ASSERTIONS']:
|
if settings['ASSERTIONS']:
|
||||||
basic_funcs += ['nullFunc']
|
basic_funcs += ['nullFunc']
|
||||||
|
@ -956,7 +956,7 @@ def emscript_fast(infile, settings, outfile, libraries=[], compiler_engine=None,
|
||||||
|
|
||||||
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
|
basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat'] + [m.replace('.', '_') for m in math_envs]
|
||||||
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
|
if settings['RESERVED_FUNCTION_POINTERS'] > 0: basic_funcs.append('jsCall')
|
||||||
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR']
|
if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE']
|
||||||
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
|
if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8']
|
||||||
if settings['ASSERTIONS']:
|
if settings['ASSERTIONS']:
|
||||||
basic_funcs += ['nullFunc']
|
basic_funcs += ['nullFunc']
|
||||||
|
|
|
@ -1327,18 +1327,22 @@ function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSa
|
||||||
var printType = type;
|
var printType = type;
|
||||||
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
|
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
|
||||||
if (printType[0] === '#') printType = printType.substr(1);
|
if (printType[0] === '#') printType = printType.substr(1);
|
||||||
return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
|
if (ASM_JS) {
|
||||||
} else {
|
if (!ignore) return asmCoercion('SAFE_HEAP_LOAD(' + asmCoercion(offset, 'i32') + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type);
|
||||||
var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
|
// else fall through
|
||||||
if (ASM_JS && (phase == 'funcs' || forceAsm)) {
|
} else {
|
||||||
ret = asmCoercion(ret, type);
|
return asmCoercion('SAFE_HEAP_LOAD(' + offset + ', ' + (ASM_JS ? 0 : printType) + ', ' + (!!unsigned+0) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')', type);
|
||||||
}
|
}
|
||||||
if (ASM_HEAP_LOG) {
|
|
||||||
ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret,
|
|
||||||
'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
var ret = makeGetSlabs(ptr, type, false, unsigned)[0] + '[' + getHeapOffset(offset, type, forceAsm) + ']';
|
||||||
|
if (ASM_JS && (phase == 'funcs' || forceAsm)) {
|
||||||
|
ret = asmCoercion(ret, type);
|
||||||
|
}
|
||||||
|
if (ASM_HEAP_LOG) {
|
||||||
|
ret = makeInlineCalculation('(asmPrint' + (type in Runtime.FLOAT_TYPES ? 'Float' : 'Int') + '(' + (asmPrintCounter++) + ',' + asmCoercion('VALUE', type) + '), VALUE)', ret,
|
||||||
|
'temp' + (type in Runtime.FLOAT_TYPES ? 'Double' : 'Int'));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeGetValueAsm(ptr, pos, type, unsigned) {
|
function makeGetValueAsm(ptr, pos, type, unsigned) {
|
||||||
|
@ -1435,10 +1439,14 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe,
|
||||||
var printType = type;
|
var printType = type;
|
||||||
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
|
if (printType !== 'null' && printType[0] !== '#') printType = '"' + safeQuote(printType) + '"';
|
||||||
if (printType[0] === '#') printType = printType.substr(1);
|
if (printType[0] === '#') printType = printType.substr(1);
|
||||||
return 'SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
|
if (ASM_JS) {
|
||||||
} else {
|
if (!ignore) return asmCoercion('SAFE_HEAP_STORE(' + asmCoercion(offset, 'i32') + ', ' + asmCoercion(value, type) + ', ' + Runtime.getNativeTypeSize(type) + ', ' + ((type in Runtime.FLOAT_TYPES)|0) + ')', type);
|
||||||
return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
|
// else fall through
|
||||||
|
} else {
|
||||||
|
return 'SAFE_HEAP_STORE(' + offset + ', ' + value + ', ' + (ASM_JS ? 0 : printType) + ', ' + ((!checkSafeHeap() || ignore)|0) + ')';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return makeGetSlabs(ptr, type, true).map(function(slab) { return slab + '[' + getHeapOffset(offset, type, forceAsm) + ']=' + value }).join(sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
|
function makeSetValueAsm(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ Module.print = Module.printErr = function(){};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SAFE_HEAP
|
#if SAFE_HEAP
|
||||||
|
#if ASM_JS == 0
|
||||||
//========================================
|
//========================================
|
||||||
// Debugging tools - Heap
|
// Debugging tools - Heap
|
||||||
//========================================
|
//========================================
|
||||||
|
@ -166,6 +167,38 @@ function SAFE_HEAP_FILL_HISTORY(from, to, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================
|
//==========================================
|
||||||
|
#else
|
||||||
|
// ASM_JS safe heap
|
||||||
|
|
||||||
|
function getSafeHeapType(bytes, isFloat) {
|
||||||
|
switch (bytes) {
|
||||||
|
case 1: return 'i8';
|
||||||
|
case 2: return 'i16';
|
||||||
|
case 4: return isFloat ? 'float' : 'i32';
|
||||||
|
case 8: return 'double';
|
||||||
|
default: assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
|
||||||
|
#if SAFE_HEAP_LOG
|
||||||
|
Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]);
|
||||||
|
#endif
|
||||||
|
assert(dest > 0, 'segmentation fault');
|
||||||
|
assert(dest % bytes === 0);
|
||||||
|
setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SAFE_HEAP_LOAD(dest, bytes, isFloat) {
|
||||||
|
#if SAFE_HEAP_LOG
|
||||||
|
Module.print('SAFE_HEAP load: ' + [dest, bytes, isFloat]);
|
||||||
|
#endif
|
||||||
|
assert(dest > 0, 'segmentation fault');
|
||||||
|
assert(dest % bytes === 0);
|
||||||
|
return getValue(dest, getSafeHeapType(bytes, isFloat), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CHECK_HEAP_ALIGN
|
#if CHECK_HEAP_ALIGN
|
||||||
|
|
|
@ -49,7 +49,7 @@ var RuntimeGenerator = {
|
||||||
stackExit: function(initial, force) {
|
stackExit: function(initial, force) {
|
||||||
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
|
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
|
||||||
var ret = '';
|
var ret = '';
|
||||||
if (SAFE_HEAP) {
|
if (SAFE_HEAP && !ASM_JS) {
|
||||||
ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }';
|
ret += 'var i = sp; while ((i|0) < (STACKTOP|0)) { SAFE_HEAP_CLEAR(i|0); i = (i+1)|0 }';
|
||||||
}
|
}
|
||||||
return ret += 'STACKTOP=sp';
|
return ret += 'STACKTOP=sp';
|
||||||
|
|
|
@ -1486,7 +1486,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
|
||||||
|
|
||||||
def test_segfault(self):
|
def test_segfault(self):
|
||||||
if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults')
|
if self.emcc_args is None: return self.skip('SAFE_HEAP without ta2 means we check types too, which hide segfaults')
|
||||||
if Settings.ASM_JS: return self.skip('asm does not support safe heap')
|
if os.environ.get('EMCC_FAST_COMPILER') == '1' and '-O2' not in self.emcc_args: return self.skip('todo in non-jsopts-enabled fastcomp')
|
||||||
|
|
||||||
Settings.SAFE_HEAP = 1
|
Settings.SAFE_HEAP = 1
|
||||||
|
|
||||||
|
|
|
@ -3888,6 +3888,104 @@ function outline(ast) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function safeHeap(ast) {
|
||||||
|
function fixPtr(ptr, heap) {
|
||||||
|
switch (heap) {
|
||||||
|
case 'HEAP8': case 'HEAPU8': break;
|
||||||
|
case 'HEAP16': case 'HEAPU16': {
|
||||||
|
if (ptr[0] === 'binary') {
|
||||||
|
assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 1);
|
||||||
|
ptr = ptr[2]; // skip the shift
|
||||||
|
} else {
|
||||||
|
ptr = ['binary', '*', ptr, ['num', 2]]; // was unshifted, convert to absolute address
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'HEAP32': case 'HEAPU32': {
|
||||||
|
if (ptr[0] === 'binary') {
|
||||||
|
assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2);
|
||||||
|
ptr = ptr[2]; // skip the shift
|
||||||
|
} else {
|
||||||
|
ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'HEAPF32': {
|
||||||
|
if (ptr[0] === 'binary') {
|
||||||
|
assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 2);
|
||||||
|
ptr = ptr[2]; // skip the shift
|
||||||
|
} else {
|
||||||
|
ptr = ['binary', '*', ptr, ['num', 4]]; // was unshifted, convert to absolute address
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'HEAPF64': {
|
||||||
|
if (ptr[0] === 'binary') {
|
||||||
|
assert(ptr[1] === '>>' && ptr[3][0] === 'num' && ptr[3][1] === 3);
|
||||||
|
ptr = ptr[2]; // skip the shift
|
||||||
|
} else {
|
||||||
|
ptr = ['binary', '*', ptr, ['num', 8]]; // was unshifted, convert to absolute address
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw 'bad heap ' + heap;
|
||||||
|
}
|
||||||
|
ptr = ['binary', '|', ptr, ['num', 0]];
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
traverseGenerated(ast, function(node, type) {
|
||||||
|
if (type === 'assign') {
|
||||||
|
if (node[1] === true && node[2][0] === 'sub') {
|
||||||
|
var heap = node[2][1][1];
|
||||||
|
var ptr = fixPtr(node[2][2], heap);
|
||||||
|
var value = node[3];
|
||||||
|
// SAFE_HEAP_STORE(ptr, value, bytes, isFloat)
|
||||||
|
switch (heap) {
|
||||||
|
case 'HEAP8': case 'HEAPU8': {
|
||||||
|
return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 1], ['num', '0']]];
|
||||||
|
}
|
||||||
|
case 'HEAP16': case 'HEAPU16': {
|
||||||
|
return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 2], ['num', '0']]];
|
||||||
|
}
|
||||||
|
case 'HEAP32': case 'HEAPU32': {
|
||||||
|
return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_INT), ['num', 4], ['num', '0']]];
|
||||||
|
}
|
||||||
|
case 'HEAPF32': {
|
||||||
|
return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_FLOAT), ['num', 4], ['num', '1']]];
|
||||||
|
}
|
||||||
|
case 'HEAPF64': {
|
||||||
|
return ['call', ['name', 'SAFE_HEAP_STORE'], [ptr, makeAsmCoercion(value, ASM_DOUBLE), ['num', 8], ['num', '1']]];
|
||||||
|
}
|
||||||
|
default: throw 'bad heap ' + heap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type === 'sub') {
|
||||||
|
var heap = node[1][1];
|
||||||
|
if (heap[0] !== 'H') return;
|
||||||
|
var ptr = fixPtr(node[2], heap);
|
||||||
|
// SAFE_HEAP_LOAD(ptr, bytes, isFloat)
|
||||||
|
switch (heap) {
|
||||||
|
case 'HEAP8': case 'HEAPU8': {
|
||||||
|
return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0']]], ASM_INT);
|
||||||
|
}
|
||||||
|
case 'HEAP16': case 'HEAPU16': {
|
||||||
|
return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0']]], ASM_INT);
|
||||||
|
}
|
||||||
|
case 'HEAP32': case 'HEAPU32': {
|
||||||
|
return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0']]], ASM_INT);
|
||||||
|
}
|
||||||
|
case 'HEAPF32': {
|
||||||
|
return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1']]], ASM_FLOAT);
|
||||||
|
}
|
||||||
|
case 'HEAPF64': {
|
||||||
|
return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1']]], ASM_DOUBLE);
|
||||||
|
}
|
||||||
|
default: throw 'bad heap ' + heap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Last pass utilities
|
// Last pass utilities
|
||||||
|
|
||||||
// Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly)
|
// Change +5 to DOT$ZERO(5). We then textually change 5 to 5.0 (uglify's ast cannot differentiate between 5 and 5.0 directly)
|
||||||
|
@ -3978,6 +4076,7 @@ function asmLastOpts(ast) {
|
||||||
var minifyWhitespace = false, printMetadata = true, asm = false, last = false;
|
var minifyWhitespace = false, printMetadata = true, asm = false, last = false;
|
||||||
|
|
||||||
var passes = {
|
var passes = {
|
||||||
|
// passes
|
||||||
dumpAst: dumpAst,
|
dumpAst: dumpAst,
|
||||||
dumpSrc: dumpSrc,
|
dumpSrc: dumpSrc,
|
||||||
unGlobalize: unGlobalize,
|
unGlobalize: unGlobalize,
|
||||||
|
@ -3996,6 +4095,9 @@ var passes = {
|
||||||
minifyLocals: minifyLocals,
|
minifyLocals: minifyLocals,
|
||||||
relocate: relocate,
|
relocate: relocate,
|
||||||
outline: outline,
|
outline: outline,
|
||||||
|
safeHeap: safeHeap,
|
||||||
|
|
||||||
|
// flags
|
||||||
minifyWhitespace: function() { minifyWhitespace = true },
|
minifyWhitespace: function() { minifyWhitespace = true },
|
||||||
noPrintMetadata: function() { printMetadata = false },
|
noPrintMetadata: function() { printMetadata = false },
|
||||||
asm: function() { asm = true },
|
asm: function() { asm = true },
|
||||||
|
|
Загрузка…
Ссылка в новой задаче