Skip to content

Commit df11fcb

Browse files
nazar-pckripken
authored andcommitted
Eval elimination #5 (#5811)
* Simplify `cwrap()` implementation: borrowed one optimization from `NO_DYNAMIC_EXECUTION == 0`, the rest doesn't make things faster * Unwrap IIFE
1 parent 8254b36 commit df11fcb

File tree

1 file changed

+74
-147
lines changed

1 file changed

+74
-147
lines changed

src/preamble.js

Lines changed: 74 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -129,167 +129,94 @@ function getCFunc(ident) {
129129
return func;
130130
}
131131

132-
var cwrap, ccall;
133-
(function(){
134-
var JSfuncs = {
135-
// Helpers for cwrap -- it can't refer to Runtime directly because it might
136-
// be renamed by closure, instead it calls JSfuncs['stackSave'].body to find
137-
// out what the minified function name is.
138-
'stackSave': function() {
139-
Runtime.stackSave()
140-
},
141-
'stackRestore': function() {
142-
Runtime.stackRestore()
143-
},
144-
// type conversion from js to c
145-
'arrayToC' : function(arr) {
146-
var ret = Runtime.stackAlloc(arr.length);
147-
writeArrayToMemory(arr, ret);
148-
return ret;
149-
},
150-
'stringToC' : function(str) {
151-
var ret = 0;
152-
if (str !== null && str !== undefined && str !== 0) { // null string
153-
// at most 4 bytes per UTF-8 code point, +1 for the trailing '\0'
154-
var len = (str.length << 2) + 1;
155-
ret = Runtime.stackAlloc(len);
156-
stringToUTF8(str, ret, len);
157-
}
158-
return ret;
132+
var JSfuncs = {
133+
// Helpers for cwrap -- it can't refer to Runtime directly because it might
134+
// be renamed by closure, instead it calls JSfuncs['stackSave'].body to find
135+
// out what the minified function name is.
136+
'stackSave': function() {
137+
Runtime.stackSave()
138+
},
139+
'stackRestore': function() {
140+
Runtime.stackRestore()
141+
},
142+
// type conversion from js to c
143+
'arrayToC' : function(arr) {
144+
var ret = Runtime.stackAlloc(arr.length);
145+
writeArrayToMemory(arr, ret);
146+
return ret;
147+
},
148+
'stringToC' : function(str) {
149+
var ret = 0;
150+
if (str !== null && str !== undefined && str !== 0) { // null string
151+
// at most 4 bytes per UTF-8 code point, +1 for the trailing '\0'
152+
var len = (str.length << 2) + 1;
153+
ret = Runtime.stackAlloc(len);
154+
stringToUTF8(str, ret, len);
159155
}
160-
};
161-
// For fast lookup of conversion functions
162-
var toC = {'string' : JSfuncs['stringToC'], 'array' : JSfuncs['arrayToC']};
163-
164-
// C calling interface.
165-
ccall = function ccallFunc(ident, returnType, argTypes, args, opts) {
166-
var func = getCFunc(ident);
167-
var cArgs = [];
168-
var stack = 0;
156+
return ret;
157+
}
158+
};
159+
// For fast lookup of conversion functions
160+
var toC = {'string' : JSfuncs['stringToC'], 'array' : JSfuncs['arrayToC']};
161+
162+
// C calling interface.
163+
function ccall (ident, returnType, argTypes, args, opts) {
164+
var func = getCFunc(ident);
165+
var cArgs = [];
166+
var stack = 0;
169167
#if ASSERTIONS
170-
assert(returnType !== 'array', 'Return type should not be "array".');
171-
#endif
172-
if (args) {
173-
for (var i = 0; i < args.length; i++) {
174-
var converter = toC[argTypes[i]];
175-
if (converter) {
176-
if (stack === 0) stack = Runtime.stackSave();
177-
cArgs[i] = converter(args[i]);
178-
} else {
179-
cArgs[i] = args[i];
180-
}
168+
assert(returnType !== 'array', 'Return type should not be "array".');
169+
#endif
170+
if (args) {
171+
for (var i = 0; i < args.length; i++) {
172+
var converter = toC[argTypes[i]];
173+
if (converter) {
174+
if (stack === 0) stack = Runtime.stackSave();
175+
cArgs[i] = converter(args[i]);
176+
} else {
177+
cArgs[i] = args[i];
181178
}
182179
}
183-
var ret = func.apply(null, cArgs);
180+
}
181+
var ret = func.apply(null, cArgs);
184182
#if ASSERTIONS
185183
#if EMTERPRETIFY_ASYNC
186-
if ((!opts || !opts.async) && typeof EmterpreterAsync === 'object') {
187-
assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling ccall');
188-
}
189-
if (opts && opts.async) assert(!returnType, 'async ccalls cannot return values');
184+
if ((!opts || !opts.async) && typeof EmterpreterAsync === 'object') {
185+
assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling ccall');
186+
}
187+
if (opts && opts.async) assert(!returnType, 'async ccalls cannot return values');
190188
#endif
191189
#endif
192-
if (returnType === 'string') ret = Pointer_stringify(ret);
193-
if (stack !== 0) {
190+
if (returnType === 'string') ret = Pointer_stringify(ret);
191+
if (stack !== 0) {
194192
#if EMTERPRETIFY_ASYNC
195-
if (opts && opts.async) {
196-
EmterpreterAsync.asyncFinalizers.push(function() {
197-
Runtime.stackRestore(stack);
198-
});
199-
return;
200-
}
201-
#endif
202-
Runtime.stackRestore(stack);
193+
if (opts && opts.async) {
194+
EmterpreterAsync.asyncFinalizers.push(function() {
195+
Runtime.stackRestore(stack);
196+
});
197+
return;
203198
}
204-
return ret;
199+
#endif
200+
Runtime.stackRestore(stack);
205201
}
202+
return ret;
203+
}
206204

207-
#if NO_DYNAMIC_EXECUTION == 0
208-
var sourceRegex = /^function\s*[a-zA-Z$_0-9]*\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;
209-
function parseJSFunc(jsfunc) {
210-
// Match the body and the return value of a javascript function source
211-
var parsed = jsfunc.toString().match(sourceRegex).slice(1);
212-
return {arguments : parsed[0], body : parsed[1], returnValue: parsed[2]}
213-
}
214-
215-
// sources of useful functions. we create this lazily as it can trigger a source decompression on this entire file
216-
var JSsource = null;
217-
function ensureJSsource() {
218-
if (!JSsource) {
219-
JSsource = {};
220-
for (var fun in JSfuncs) {
221-
if (JSfuncs.hasOwnProperty(fun)) {
222-
// Elements of toCsource are arrays of three items:
223-
// the code, and the return value
224-
JSsource[fun] = parseJSFunc(JSfuncs[fun]);
225-
}
226-
}
227-
}
205+
function cwrap (ident, returnType, argTypes) {
206+
argTypes = argTypes || [];
207+
var cfunc = getCFunc(ident);
208+
// When the function takes numbers and returns a number, we can just return
209+
// the original function
210+
var numericArgs = argTypes.every(function(type){ return type === 'number'});
211+
var numericRet = returnType !== 'string';
212+
if (numericRet && numericArgs) {
213+
return cfunc;
228214
}
229-
230-
cwrap = function cwrap(ident, returnType, argTypes) {
231-
argTypes = argTypes || [];
232-
var cfunc = getCFunc(ident);
233-
// When the function takes numbers and returns a number, we can just return
234-
// the original function
235-
var numericArgs = argTypes.every(function(type){ return type === 'number'});
236-
var numericRet = (returnType !== 'string');
237-
if ( numericRet && numericArgs) {
238-
return cfunc;
239-
}
240-
// Creation of the arguments list (["$1","$2",...,"$nargs"])
241-
var argNames = argTypes.map(function(x,i){return '$'+i});
242-
var funcstr = "(function(" + argNames.join(',') + ") {";
243-
var nargs = argTypes.length;
244-
if (!numericArgs) {
245-
// Generate the code needed to convert the arguments from javascript
246-
// values to pointers
247-
ensureJSsource();
248-
funcstr += 'var stack = ' + JSsource['stackSave'].body + ';';
249-
for (var i = 0; i < nargs; i++) {
250-
var arg = argNames[i], type = argTypes[i];
251-
if (type === 'number') continue;
252-
var convertCode = JSsource[type + 'ToC']; // [code, return]
253-
funcstr += 'var ' + convertCode.arguments + ' = ' + arg + ';';
254-
funcstr += convertCode.body + ';';
255-
funcstr += arg + '=(' + convertCode.returnValue + ');';
256-
}
257-
}
258-
259-
// When the code is compressed, the name of cfunc is not literally 'cfunc' anymore
260-
var cfuncname = parseJSFunc(function(){return cfunc}).returnValue;
261-
// Call the function
262-
funcstr += 'var ret = ' + cfuncname + '(' + argNames.join(',') + ');';
263-
if (!numericRet) { // Return type can only by 'string' or 'number'
264-
// Convert the result to a string
265-
var strgfy = parseJSFunc(function(){return Pointer_stringify}).returnValue;
266-
funcstr += 'ret = ' + strgfy + '(ret);';
267-
}
268-
#if ASSERTIONS
269-
funcstr += "if (typeof EmterpreterAsync === 'object') { assert(!EmterpreterAsync.state, 'cannot start async op with normal JS calling cwrap') }";
270-
#endif
271-
if (!numericArgs) {
272-
// If we had a stack, restore it
273-
ensureJSsource();
274-
funcstr += JSsource['stackRestore'].body.replace('()', '(stack)') + ';';
275-
}
276-
funcstr += 'return ret})';
277-
return eval(funcstr);
278-
};
279-
#else
280-
// NO_DYNAMIC_EXECUTION is on, so we can't use the fast version of cwrap.
281-
// Fall back to returning a bound version of ccall.
282-
cwrap = function cwrap(ident, returnType, argTypes) {
283-
return function() {
284-
#if ASSERTIONS
285-
Runtime.warnOnce('NO_DYNAMIC_EXECUTION was set, '
286-
+ 'using slow cwrap implementation');
287-
#endif
288-
return ccall(ident, returnType, argTypes, arguments);
289-
}
215+
return function() {
216+
return ccall(ident, returnType, argTypes, arguments);
290217
}
291-
#endif
292-
})();
218+
}
219+
293220
{{{ maybeExport("ccall") }}}
294221
{{{ maybeExport("cwrap") }}}
295222

0 commit comments

Comments
 (0)