aboutsummaryrefslogtreecommitdiff
path: root/src/experimental/functypeopt.diff
blob: 6e2fa39654080433c846079448c3f28f9a861a8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
diff --git a/src/parseTools.js b/src/parseTools.js
index 9786460..fb4be9f 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -205,26 +205,57 @@ function isFunctionDef(token, out) {
 function isPossiblyFunctionType(type) {
   // A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite.
   var len = type.length;
-  return type[len-2] == ')' && type[len-1] == '*';
+  return type[len-2] == ')' && type[len-1] == '*' && type.indexOf('(') > 0;
 }
 
 function isFunctionType(type, out) {
   if (!isPossiblyFunctionType(type)) return false;
   type = type.replace(/"[^"]+"/g, '".."');
-  var parts;
   // hackish, but quick splitting of function def parts. this must be fast as it happens a lot
-  if (type[0] != '[') {
-    parts = type.split(' ');
-  } else {
-    var index = type.search(']');
-    index += type.substr(index).search(' ');
-    parts = [type.substr(0, index), type.substr(index+1)];
-  }
+  var parts = type.split(' ');
   if (pointingLevels(type) !== 1) return false;
   var text = removeAllPointing(parts.slice(1).join(' '));
   if (!text) return false;
-  if (out) out.returnType = parts[0];
-  return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out);
+  if (!isType(parts[0])) return false;
+  var level = 0;
+  var chunks = [];
+  var currStart = 0;
+  for (var i = 0; i < type.length; i++) {
+    var curr = type[i];
+    if (curr == '(') {
+      level++;
+      if (level == 1) {
+        chunks.push(type.substring(currStart, i));
+        currStart = i+1;
+      }
+    } else if (curr == ')') {
+      level--;
+      if (level == 0) {
+        curr = type.substring(currStart, i);
+        if (curr == '') curr = '$'; // make sure inside of () stays valid
+        chunks.push(curr);
+        currStart = i+1;
+      }
+    }
+  }
+//printErr('pre chunks ' + JSON.stringify(chunks));
+  chunks = chunks.map(function(chunk) { return chunk.replace(/ /g, '') })
+                 .filter(function(chunk) { return chunk.length > 0 })
+                 .map(function(chunk) { return chunk.replace(/\$/g, ' ') });
+//printErr('post chunks ' + JSON.stringify(chunks));
+  switch (chunks.length) {
+    case 2: { // e.g.  void (i32,i32)
+      if (out) out.returnType = chunks[0]; // TODO: add cache, with this as the value
+      return isFunctionDef({ text: '(' + chunks[1] + ')', item: tokenize(chunks[1], true) }, out);
+    }
+    case 4: { // e.g.  void (i32)* (i32, i32)  (i.e., returns void (i32)*!)
+      if (chunks[2] != '*') return false;
+      if (out) out.returnType = chunks[0] + ' ' + chunks[1];
+      return isFunctionDef({ text: '(' + chunks[1] + ')', item: tokenize(chunks[1], true) }) &&
+             isFunctionDef({ text: '(' + chunks[2] + ')', item: tokenize(chunks[2], true) }, out);
+    }
+  }
+  return false;
 }
 
 var isTypeCache = {}; // quite hot, optimize as much as possible
diff --git a/tests/runner.py b/tests/runner.py
index 842daca..312541f 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -2860,6 +2860,35 @@ Exiting setjmp function, level: 0, prev_jmp: -1
           '''
         self.do_run(src, 'fn2(-5) = 5, fn(10) = 3.16')
 
+    def test_funcptrfunc(self):
+      src = r'''
+        #include <stdio.h>
+
+/*
+define internal fastcc void ()* @sqlite3OsDlSym(%struct.sqlite3_vfs* %pVfs, i8* %pHdle, i8* %zSym) nounwind {
+  %1 = getelementptr inbounds %struct.sqlite3_vfs* %pVfs, i32 0, i32 12
+  %2 = load void ()* (%struct.sqlite3_vfs*, i8*, i8*)** %1, align 4
+  %3 = tail call void ()* (%struct.sqlite3_vfs*, i8*, i8*)* %2(%struct.sqlite3_vfs* %pVfs, i8* %pHdle, i8* %zSym) nounwind
+  ret void ()* %3
+}
+*/
+
+        typedef void (*funcptr)(int, int);
+        typedef funcptr (*funcptrfunc)(int);
+
+        funcptr __attribute__ ((noinline)) getIt(int x) {
+          return (funcptr)x;
+        }
+
+        int main(int argc, char **argv)
+        {
+          funcptrfunc fpf = argc < 100 ? getIt : NULL;
+          printf("*%p*\n", fpf(argc));
+          return 0;
+        }
+      '''
+      self.do_run(src, '*0x1*')
+
     def test_emptyclass(self):
         if self.emcc_args is None: return self.skip('requires emcc')
         src = '''