├── JSWriter.exports
├── objc2js
├── Test
├── arithmetic.m
├── static.m
├── enum.m
├── call.m
├── simple.m
├── union.m
├── struct.m
├── NSString.js
├── pointer.m
├── jstest.html
├── jstest.m
├── NSObject.js
├── objc_runtime.js
└── c_support.js
├── Makefile
├── README.txt
└── JSWriter.cpp
/JSWriter.exports:
--------------------------------------------------------------------------------
1 | _ZN4llvm8Registry*
2 |
--------------------------------------------------------------------------------
/objc2js:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | clang -cc1 -load `llvm-config --libdir`/libJSWriter.so -triple i386-unknown-unknown -plugin -emit-js $@
3 |
--------------------------------------------------------------------------------
/Test/arithmetic.m:
--------------------------------------------------------------------------------
1 | void jsalert(int);
2 |
3 | int main(void)
4 | {
5 | signed char a = 127;
6 | a++;
7 | //a = a + 5;
8 | jsalert(a);
9 | }
10 |
--------------------------------------------------------------------------------
/Test/static.m:
--------------------------------------------------------------------------------
1 | static int j;
2 | int test(void)
3 | {
4 | static int i = 0;
5 | return (++i) + (++j);
6 | }
7 | void jsalert(int);
8 |
9 | int main(void)
10 | {
11 | test();
12 | test();
13 | jsalert(test());
14 | }
15 |
--------------------------------------------------------------------------------
/Test/enum.m:
--------------------------------------------------------------------------------
1 | enum test
2 | {
3 | one = 1,
4 | two = 2,
5 | three = one + two
6 | };
7 | void jsalert(enum test);
8 |
9 | int main(void)
10 | {
11 | jsalert(one);
12 | jsalert(two);
13 | jsalert(three);
14 | return 0;
15 | }
16 |
--------------------------------------------------------------------------------
/Test/call.m:
--------------------------------------------------------------------------------
1 |
2 | void jsalert(char);
3 |
4 | void passedPointer(char *x)
5 | {
6 | *x = 'a';
7 | }
8 | void passedValue(char x)
9 | {
10 | x = 'b';
11 | }
12 |
13 | int main(void)
14 | {
15 | char a;
16 | passedPointer(&a);
17 | passedValue(a);
18 | jsalert(a);
19 | }
20 |
--------------------------------------------------------------------------------
/Test/simple.m:
--------------------------------------------------------------------------------
1 | struct foo
2 | {
3 | int c;
4 | struct
5 | {
6 | id a;
7 | } b;
8 | } s;
9 | void jsalert(const char*);
10 |
11 | @interface NSObject
12 | +new;
13 | -alert;
14 | @end
15 |
16 | int main(void)
17 | {
18 | s.b.a = [NSObject new];
19 | struct foo bar = { 12, { 0 }};
20 | [s.b.a alert];
21 | id x;
22 | [x alert];
23 | }
24 |
--------------------------------------------------------------------------------
/Test/union.m:
--------------------------------------------------------------------------------
1 | void *malloc(unsigned int);
2 | void jsalert(int);
3 | @interface NSObject
4 | +new;
5 | -alert;
6 | @end
7 |
8 | union foo
9 | {
10 | int a;
11 | id b;
12 | };
13 |
14 | int main(void)
15 | {
16 | union foo*array = malloc(10*sizeof(union foo));
17 | array[5].a = 12;
18 | array[0].b = [NSObject new];
19 |
20 | jsalert(array[0].a);
21 | jsalert(array[5].a);
22 | [array[0].b alert];
23 |
24 | return 0;
25 | }
26 |
--------------------------------------------------------------------------------
/Test/struct.m:
--------------------------------------------------------------------------------
1 | void *malloc(unsigned int);
2 | void jsalert(int);
3 | @interface NSObject
4 | +new;
5 | -alert;
6 | @end
7 |
8 | struct foo
9 | {
10 | int a;
11 | id b;
12 | };
13 |
14 | int main(void)
15 | {
16 | struct foo*array = malloc(10*sizeof(struct foo));
17 | array[5].a = 12;
18 | array[0].b = [NSObject new];
19 |
20 | jsalert(array[0].a);
21 | jsalert(array[5].a);
22 | [array[0].b alert];
23 |
24 | return 0;
25 | }
26 |
--------------------------------------------------------------------------------
/Test/NSString.js:
--------------------------------------------------------------------------------
1 |
2 | // Stub implementations: should be replaced by the GNUstep ones!
3 | OBJC.NSString = new ObjCClass("NSString", "NSObject")
4 | OBJC.NSMutableString = new ObjCClass("NSMutableString", "NSString")
5 |
6 |
7 | OBJC.JSString = new ObjCClass("JSString", "NSString")
8 | OBJC.JSString.methods["length"] = function(self, _cmd) { return self.length; };
9 | OBJC.JSString.methods["characterAt:"] = function(self, _cmd, i) { return self[i]; };
10 |
11 | OBJC.NSConstantString = new ObjCClass("NSConstantString", "JSString")
12 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ##===- examples/PrintFunctionNames/Makefile ----------------*- Makefile -*-===##
2 | #
3 | # The LLVM Compiler Infrastructure
4 | #
5 | # This file is distributed under the University of Illinois Open Source
6 | # License. See LICENSE.TXT for details.
7 | #
8 | ##===----------------------------------------------------------------------===##
9 |
10 | CLANG_LEVEL := ../
11 | LIBRARYNAME = JSWriter
12 |
13 | # If we don't need RTTI or EH, there's no reason to export anything
14 | # from the plugin.
15 | ifneq ($(REQUIRES_RTTI), 1)
16 | ifneq ($(REQUIRES_EH), 1)
17 | EXPORTED_SYMBOL_FILE = JSWriter.exports
18 | endif
19 | endif
20 |
21 | LINK_LIBS_IN_SHARED = 0
22 | SHARED_LIBRARY = 1
23 |
24 | include $(CLANG_LEVEL)/Makefile
25 |
--------------------------------------------------------------------------------
/Test/pointer.m:
--------------------------------------------------------------------------------
1 | void *malloc(unsigned int);
2 | void jsalert(int);
3 | @interface NSObject
4 | +new;
5 | -alert;
6 | @end
7 |
8 | int main(void)
9 | {
10 | int i = 1;
11 | jsalert((int)*(float*)&i);
12 | i = i + 1;
13 | jsalert(i);
14 | i += 1;
15 | jsalert(i);
16 | jsalert(i++);
17 | jsalert(i);
18 | char *str = "abc";
19 | char *ptr = str++;
20 | jsalert(*ptr);
21 | int *array = malloc(1024);
22 | float *alias;
23 | alias = (float*)array;
24 | alias[2] = 1;
25 | array[1] = alias[2];
26 | ((id*)array)[12] = [NSObject new];
27 | jsalert(sizeof(array));
28 | jsalert(array[1]);
29 | jsalert(array[2]);
30 | jsalert(array[12]);
31 | jsalert(*((array+13) - 1));
32 | i = 12;
33 | jsalert(array[i]);
34 |
35 | [((id*)alias)[12] alert];
36 |
37 | return 0;
38 | }
39 |
--------------------------------------------------------------------------------
/Test/jstest.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 | Testing JavaScript Generated From Objective-C
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Test/jstest.m:
--------------------------------------------------------------------------------
1 | void *malloc(unsigned int);
2 | void jsalert(const char*);
3 |
4 | @interface NSObject
5 | +alloc;
6 | +new;
7 | -init;
8 | -retain;
9 | -(void)release;
10 | -(void)alert;
11 | @end
12 | @interface Test : NSObject
13 | @property int foo;
14 | @end
15 | @implementation Test
16 | @synthesize foo;
17 | - self { return self; }
18 | @end
19 |
20 | struct s
21 | {
22 | int a;
23 | float b;
24 | const char *s;
25 | id obj;
26 | };
27 |
28 | void takesPointer(struct s*v)
29 | {
30 | v->s = "Correctly set";
31 | }
32 | void takesCopy(struct s v)
33 | {
34 | v.s = "Incorrectly set";
35 | }
36 |
37 | #define FOO(x) do { x; } while(0)
38 |
39 | int bar(int a) { return 2*a; }
40 |
41 | int main(int b, char**argv)
42 | {
43 | int a = 12, c;
44 | c = (a + b) * bar(a);
45 | for (int i=0 ; i< 100 ; i++)
46 | {
47 | int j;
48 | while ((j = (a < 12)))
49 | {
50 | do {
51 | a++;
52 | } while (0);
53 | }
54 | }
55 | FOO(a++);
56 | Test *f = [Test new];
57 | f.foo += 1;
58 | [f alert];
59 |
60 | int *array = malloc(1024);
61 | float *alias = (float*)array;
62 | array[1] = alias[2];
63 |
64 | struct s structVarNoInit;
65 | struct s structVar = { .b = 1,.a= 2, .s ="C string", .obj = @"ObjC String"};
66 | int array2[12];
67 | int element = array2[1];
68 | takesPointer(&structVar);
69 | takesCopy(structVar);
70 | jsalert(structVar.s);
71 | }
72 |
--------------------------------------------------------------------------------
/Test/NSObject.js:
--------------------------------------------------------------------------------
1 |
2 | C.NSAllocateObject = function(cls) {
3 | return cloneObject(cls.template);
4 | };
5 | C.NSDeallocateObject = function(obj) {}
6 |
7 | OBJC.NSObject = new ObjCClass("NSObject", null)
8 |
9 | // NSObject class methods
10 | OBJC.NSObject.isa.methods["alloc"] = function(self, _cmd) { return C.NSAllocateObject(self); };
11 | OBJC.NSObject.isa.methods["allocWithZone"] = function(self, _cmd) { return C.NSAllocateObject(self); };
12 | OBJC.NSObject.isa.methods["new"] = function(self, _cmd) { return objc_msgSend(objc_msgSend(self, "alloc"), "init"); }
13 |
14 |
15 | // NSObject instance methods
16 |
17 | OBJC.NSObject.methods["copy"] = function(self, _cmd) { return objc_msgSend(self, "copyWithZone:", nil); };
18 | OBJC.NSObject.methods["dealloc"] = function(self, _cmd) { C.NSDeallocateObject(self); }
19 | OBJC.NSObject.methods["class"] = function(self, _cmd) { return self.isa; }
20 |
21 | OBJC.NSObject.methods["retain"] = function(self, _cmd) { return self; };
22 | OBJC.NSObject.methods["init"] = function(self, _cmd) { return self; };
23 | OBJC.NSObject.methods["self"] = function(self, _cmd) { return self; };
24 | OBJC.NSObject.methods["release"] = function(self, _cmd) {};
25 | OBJC.NSObject.methods["description"] = function(self, _cmd) { return makeObjCString(self.isa.name);};
26 | OBJC.NSObject.methods["alert"] = function(self, _cmd) { alert(objc_msgSend(self, "description")); };
27 |
--------------------------------------------------------------------------------
/Test/objc_runtime.js:
--------------------------------------------------------------------------------
1 |
2 | function objc_msgSend(object, selector)
3 | {
4 | if (!object) return null;
5 | var method = object.isa.methods[selector];
6 | if (method)
7 | return method.apply(object, arguments);
8 | if (object.isa.methods.forwardingTargetForSelector)
9 | {
10 | arguments[0] = object.isa.methods.forwardingTargetForSelector(object, "forwardingTargetForSelector");
11 | return objc_msgSend.apply(this, arguments);
12 | }
13 | if (object.isa.methods.forwardInvocation)
14 | {
15 | // FIXME: Make the third argument an NSInvocation, not an array
16 | return object.isa.methods.forwardInvocation(object, "forwardInvocation", arguments);
17 | }
18 | // TODO: Other lookup failure mechanisms (Cocoa allows classes to add a
19 | // method when a missing one is required.
20 | // FIXME: throw some kind of exception
21 | }
22 |
23 | function objc_msgSendSuper(isClassMsg, cls, object, selector)
24 | {
25 | var method;
26 | if (isClassMsg)
27 | {
28 | method = OBJC[cls].isa.isa.methods[selector];
29 | }
30 | else
31 | {
32 | method = OBJC[cls].isa.methods[selector];
33 | }
34 | // Discard the first two elements
35 | arguments.shift();
36 | arguments.shift();
37 | method.apply(object, arguments);
38 | }
39 |
40 | function objc_initClass(cls, superClass, name)
41 | {
42 | cls.template = new Object();
43 | cls.template.isa = cls;
44 | cls.methods = new Object();
45 | cls.ivars = new Array();
46 | cls.protocols = new Array();
47 | cls.properties = new Array();
48 | cls.name = name;
49 | if (superClass)
50 | {
51 | cls.methods = cloneObject(superClass.methods);
52 | cls.superclass = superClass;
53 | }
54 | }
55 |
56 | function ObjCClass(name, superclassName)
57 | {
58 | var superClass = OBJC[superclassName];
59 | objc_initClass(this, superClass, name);
60 | this.isa = new Object();
61 | objc_initClass(this.isa, superClass ? superClass.isa : null, name);
62 | }
63 |
64 |
65 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | Objective-C to JavaScript Converter
2 | ===================================
3 |
4 | This is a clang plugin that traverses the AST and emits JavaScript code
5 | corresponding to an Objective-C compilation unit, along with a JavaScript
6 | Objective-C runtime and set of core classes. The goal of this project is to
7 | allow Objective-C applications to be compiled to JavaScript and run unmodified.
8 |
9 | In practice, this goal is unreachable, however we should be able to define a
10 | safe subset of Objective-C that works both as native code and when run in a
11 | JavaScript VM.
12 |
13 | Current Status
14 | --------------
15 |
16 | This code is VERY work-in-progress. If it works at all, then you should be
17 | surprised! Not everything in the How It Works section of this document is
18 | actually implemented!
19 |
20 | Compiling
21 | ---------
22 |
23 | Unfortunately, the clang build system does not currently make any sensible
24 | provision for building plugins outside of the clang tree. You must, therefore,
25 | check out or copy this directory inside the clang source tree.
26 |
27 | Once this is done, it can be built by running GNU make (either make or gmake,
28 | depending on your platform).
29 |
30 | How It Works (in theory)
31 | ------------------------
32 |
33 | The [Objective-]C type system doesn't really play nicely with the JavaScript,
34 | but the compiler and runtime try to work around this, performing the following
35 | mappings:
36 |
37 | - C primitive types are represented by JavaScript primitives. All C number
38 | types are JavaScript numbers, which are double-precision floating-point
39 | values. Cast operations are implemented by truncating the value to fit in
40 | the destination type.
41 |
42 | - C structured types are implemented using WebGL ArrayBuffer objects. If you
43 | allocate a structure or an array, you get an ArrayBuffer.
44 |
45 | - Pointers are implemented as AddressOf objects. These implement pointer
46 | arithmetic and dereferencing. When you take the address of an object, you
47 | get a new AddressOf object, whose pointee field is set to the object whose
48 | address you took. The result of a pointer arithmetic expression is a new
49 | AddressOf object, as long as the pointee is a valid address
50 |
51 | - Casting pointers to integers gives a unique 32-bit integer value. There is
52 | no mechanism for casting integers to pointers, and it is not possible to
53 | implement one without JavaScript gaining support for weak references.
54 |
55 | - If you attempt to store a pointer in memory buffer (i.e. a C array or
56 | structure), then the underlying buffer object has the pointer stored as a
57 | property and the integer value written into the buffer at that address. This
58 | allows you to store a pointer in a structure or union (for example) and then
59 | access its value as an integer. If you attempt to cast from this integer
60 | back to a pointer, then you get undefined behaviour. If you simply
61 | dereference the pointer as a pointer type, however, then you will load back
62 | the pointer value stored in the buffer.
63 |
64 | - Objective-C objects are JavaScript objects, with an isa field set to a class.
65 | The classes are all added to a global OBJC object, allowing class lookup to
66 | work without seeing JavaScript objects. Objective-C methods are JavaScript
67 | functions, with self and _cmd parameters. These are looked up by looking at
68 | the class's methods field, which is an object whose prototype is the
69 | superclass's methods field. This means that inheritance works as expected.
70 | Methods are called by the objc_msgSend() function, which performs the lookup
71 | and then calls the function. This also allows -forwardInvocation: and
72 | friends to work.
73 |
74 | - C functions are translated to JavaScript functions and added to fields of the
75 | C global object.
76 |
--------------------------------------------------------------------------------
/Test/c_support.js:
--------------------------------------------------------------------------------
1 | C = new Object();
2 | OBJC = new Object();
3 | CStatics = new Object();
4 |
5 | function cloneObject(obj)
6 | {
7 | var clone = function() {};
8 | clone.prototype = obj;
9 | return new clone();
10 | }
11 |
12 |
13 | /**
14 | * We can't generate pointers for Objective-C objects, but sometimes
15 | */
16 | var nextPointer = 1;
17 |
18 | Object.prototype.getPointerValue = function()
19 | {
20 | if (!this.__PointerValue)
21 | {
22 | this.__PointerValue = nextPointer++;
23 | }
24 | return this.__PointerValue;
25 | }
26 | /**
27 | * We add accessors to both ArrayBuffer and TypedArray that allow us to get any
28 | * value type that we want from an offset.
29 | */
30 | ArrayBuffer.prototype.getInt8 = function(offset)
31 | {
32 | return (new Int8Array(this))[offset];
33 | }
34 |
35 | ArrayBuffer.prototype.getInt16 = function(offset)
36 | {
37 | return (new Int16Array(this))[offset/2];
38 | }
39 |
40 | ArrayBuffer.prototype.getInt32 = function(offset)
41 | {
42 | return (new Int32Array(this))[offset / 4];
43 | }
44 |
45 | ArrayBuffer.prototype.getUint8 = function(offset)
46 | {
47 | return (new Uint8Array(this))[offset];
48 | }
49 |
50 | ArrayBuffer.prototype.getUint16 = function(offset)
51 | {
52 | return (new Uint16Array(this))[offset / 2];
53 | }
54 |
55 | ArrayBuffer.prototype.getUint32 = function(offset)
56 | {
57 | return (new Uint32Array(this))[offset / 4];
58 | }
59 |
60 | ArrayBuffer.prototype.getFloat32 = function(offset)
61 | {
62 | return (new Float32Array(this))[offset / 4];
63 | }
64 |
65 | ArrayBuffer.prototype.getFloat64 = function(offset)
66 | {
67 | return (new Float64Array(this))[offset / 8];
68 | }
69 |
70 | ArrayBuffer.prototype.setInt8 = function(offset, obj)
71 | {
72 | (new Int8Array(this))[offset] = obj;
73 | }
74 |
75 | ArrayBuffer.prototype.setInt16 = function(offset, obj)
76 | {
77 | (new Int16Array(this))[offset/2] = obj;
78 | }
79 |
80 | ArrayBuffer.prototype.setInt32 = function(offset, obj)
81 | {
82 | (new Int32Array(this))[offset/4] = obj;
83 | }
84 |
85 | ArrayBuffer.prototype.setUint8 = function(offset, obj)
86 | {
87 | (new Uint8Array(this))[offset] = obj;
88 | }
89 |
90 | ArrayBuffer.prototype.setUint16 = function(offset, obj)
91 | {
92 | (new Uint16Array(this))[offset/2] = obj;
93 | }
94 |
95 | ArrayBuffer.prototype.setUint32 = function(offset, obj)
96 | {
97 | (new Uint32Array(this))[offset/4] = obj;
98 | }
99 |
100 | ArrayBuffer.prototype.setFloat32 = function(offset, obj)
101 | {
102 | (new Float32Array(this))[offset/4] = obj;
103 | }
104 |
105 | ArrayBuffer.prototype.setFloat64 = function(offset, obj)
106 | {
107 | (new Float64Array(this))[offset/8] = obj;
108 | }
109 |
110 |
111 | ArrayBuffer.prototype.toString = function()
112 | {
113 | switch (this.byteLength)
114 | {
115 | default:
116 | return "[" + this.byteLength + " bytes]";
117 | case 1:
118 | return "'" + String.fromCharCode(this.getInt8(0)) + "'";
119 | case 2:
120 | return this.getInt16(0).toString();
121 | case 4:
122 | return this.getInt16(0).toString();
123 | }
124 | }
125 |
126 |
127 |
128 |
129 | Int8Array.prototype.getInt8 = function(offset)
130 | {
131 | return this[offset];
132 | }
133 |
134 | Int8Array.prototype.getInt16 = function(offset)
135 | {
136 | return this.buffer.getInt16(offset + this.byteOffset);
137 | }
138 |
139 | Int8Array.prototype.getInt32 = function(offset)
140 | {
141 | return this.buffer.getInt32(offset + this.byteOffset);
142 | }
143 |
144 | Int8Array.prototype.getUint8 = function(offset)
145 | {
146 | return this.buffer.getUint8(offset + this.byteOffset);
147 | }
148 |
149 | Int8Array.prototype.getUint16 = function(offset)
150 | {
151 | return this.buffer.getUint16(offset + this.byteOffset);
152 | }
153 |
154 | Int8Array.prototype.getUint32 = function(offset)
155 | {
156 | return this.buffer.getUint32(offset + this.byteOffset);
157 | }
158 |
159 | Int8Array.prototype.getFloat32 = function(offset)
160 | {
161 | return this.buffer.getFloat32(offset + this.byteOffset);
162 | }
163 |
164 | Int8Array.prototype.getFloat64 = function(offset)
165 | {
166 | return this.buffer.getFloat64(offset + this.byteOffset);
167 | }
168 |
169 | Int8Array.prototype.setInt8 = function(offset, obj)
170 | {
171 | this[offset] = obj;
172 | }
173 |
174 | Int8Array.prototype.setInt16 = function(offset, obj)
175 | {
176 | this.buffer.setInt16(offset + this.byteOffset, obj);
177 | }
178 |
179 | Int8Array.prototype.setInt32 = function(offset, obj)
180 | {
181 | this.buffer.setInt32(offset + this.byteOffset, obj);
182 | }
183 |
184 | Int8Array.prototype.setUint8 = function(offset, obj)
185 | {
186 | this.buffer.setUint8(offset + this.byteOffset, obj);
187 | }
188 |
189 | Int8Array.prototype.setUint16 = function(offset, obj)
190 | {
191 | this.buffer.setUint16(offset + this.byteOffset, obj);
192 | }
193 |
194 | Int8Array.prototype.setUint32 = function(offset, obj)
195 | {
196 | this.buffer.setUint32(offset + this.byteOffset, obj);
197 | }
198 |
199 | Int8Array.prototype.setFloat32 = function(offset, obj)
200 | {
201 | this.buffer.setFloat32(offset + this.byteOffset, obj);
202 | }
203 |
204 | Int8Array.prototype.setFloat64 = function(offset, obj)
205 | {
206 | this.buffer.setFloat64(offset + this.byteOffset, obj);
207 | }
208 |
209 |
210 | /**
211 | * Adds a pointer accessor to all objects
212 | */
213 | Object.prototype.setPointer = function(offset, object)
214 | {
215 | this[offset] = object;
216 | }
217 |
218 | ArrayBuffer.prototype.getPointer = function(offset)
219 | {
220 | return this.pointers[offset];
221 | }
222 | ArrayBuffer.prototype.setPointer = function(offset, object)
223 | {
224 | var buffer=this;
225 | // Store a fake pointer value, just in case someone wants to do some
226 | // pointer comparisons
227 | this.setInt32(offset, object.getPointerValue());
228 | // Store the real object as a property of the buffer, so we can retrieve it
229 | // again later
230 | if (!this.pointers)
231 | {
232 | this.pointers = new Object();
233 | }
234 | this.pointers[offset] = object;
235 | }
236 |
237 | Int8Array.prototype.setPointer = function(offset, object)
238 | {
239 | var buffer = this.buffer;
240 | if (this.byteOffset)
241 | {
242 | offset += this.byteOffset;
243 | }
244 | buffer.setPointer(offset, object);
245 | }
246 |
247 | Object.prototype.getPointer = function(offset)
248 | {
249 | return this[offset];
250 | }
251 | /**
252 | * Retrieves an Objective-C object value stored in a structure / array. The
253 | * actual value stored is going to be a fake value, but when we cast out we
254 | * want to retrieve the original pointer.
255 | */
256 | Int8Array.prototype.getPointer = function getPointer(offset)
257 | {
258 | return this.buffer.getPointer(offset + this.byteOffset);
259 | }
260 |
261 | ArrayBuffer.prototype.pointerAdd = function(offset)
262 | {
263 | var view = new Int8Array(this, offset);
264 | view.buffer = this;
265 | return view;
266 | }
267 | Int8Array.prototype.pointerAdd = function(offset)
268 | {
269 | var view = new Int8Array(this.buffer, this.byteOffset + offset);
270 | view.buffer = this.buffer;
271 | return view;
272 | }
273 |
274 | function OutOfRangeAddress(offset)
275 | {
276 | this.byteOffset = offset;
277 | }
278 |
279 | function AddressOf(obj)
280 | {
281 | this.pointee = obj;
282 | }
283 | AddressOf.prototype.pointerAdd = function(offset)
284 | {
285 | return new AddressOf(this.pointee.pointerAdd(offset));
286 | }
287 |
288 | AddressOf.prototype.dereference = function()
289 | {
290 | return this.pointee;
291 | }
292 | AddressOf.prototype.getPointer= function(offset)
293 | {
294 | // FIXME: error if offset != 0
295 | return this.pointee;
296 | }
297 |
298 | AddressOf.prototype.copy = function()
299 | {
300 | var n = new AddressOf(this.pointee);
301 | }
302 |
303 | AddressOf.prototype.dereference = function()
304 | {
305 | // This may be another AddressOf object.
306 | return this.pointee;
307 | }
308 |
309 | AddressOf.prototype.toString = function()
310 | {
311 | return "&(" + this.pointee + ')';
312 | }
313 |
314 | /**
315 | * Constructs a C string from a JavaScript string
316 | */
317 | function makeCString(str)
318 | {
319 | var buffer = new ArrayBuffer(str.length);
320 | var c_str = new Int8Array(buffer);
321 | c_str.buffer = buffer;
322 | for (var i=0 ; i
27 | #include
28 |
29 | using namespace clang;
30 |
31 | namespace {
32 |
33 |
34 |
35 | class JSWriter : public ASTConsumer,
36 | public RecursiveASTVisitor {
37 | llvm::raw_fd_ostream OS;
38 | ASTContext *Ctx;
39 | std::string path;
40 |
41 | public:
42 | JSWriter(CompilerInstance &CI, llvm::StringRef InFile) : ASTConsumer(),
43 | RecursiveASTVisitor(), OS(STDOUT_FILENO, false, true) {
44 | }
45 | virtual void HandleTranslationUnit(ASTContext &C) {
46 | Ctx = &C;
47 | SourceManager &SM = Ctx->getSourceManager();
48 | const FileEntry *mainFile = SM.getFileEntryForID(SM.getMainFileID());
49 | path = std::string(mainFile->getDir()->getName()) + '/' + mainFile->getName();
50 | // Emit an object for holding file-static declarations
51 | OS << "CStatics[\"" << path << "\"] = new Object();\n";
52 | TraverseDecl(C.getTranslationUnitDecl());
53 | Ctx = 0;
54 | }
55 | bool TraverseFunctionDecl(FunctionDecl *D) {
56 | if (!D->getBody()) { return true; }
57 | // FIXME: Static functions need moving to a file namespace
58 | OS << "C." << D->getNameAsString() << " = function(";
59 | for (FunctionDecl::param_iterator i=D->param_begin(),e=D->param_end() ;
60 | i!=e ; i++) {
61 | OS << (*i)->getName();
62 | if (i+1 != e) {
63 | OS << ',';
64 | }
65 | }
66 | OS << ") ";
67 | TraverseStmt(D->getBody());
68 | return true;
69 | }
70 | const char *JSPrimitiveForType(QualType Ty) {
71 | if (Ty->isIntegerType()) {
72 | if (Ty->isUnsignedIntegerType()) {
73 | switch (Ctx->getTypeSize(Ty))
74 | {
75 | case 8: return "Uint8";
76 | case 16: return "Uint16";
77 | case 32: return "Uint32";
78 | case 64: //FIXME: Emit error, no 64-bit int types in JS
79 | default: return 0;
80 | }
81 | } else {
82 | switch (Ctx->getTypeSize(Ty))
83 | {
84 | case 8: return "Int8";
85 | case 16: return "Int16";
86 | case 32: return "Int32";
87 | case 64: //FIXME: Emit error, no 64-bit int types in JS
88 | default: return 0;
89 | }
90 | }
91 | } else if (Ty->isFloatingType()) {
92 | switch (Ctx->getTypeSize(Ty))
93 | {
94 | case 32: return "Float32";
95 | case 64: return "Float64";
96 | default: return 0;
97 | }
98 | } else if (Ty->isAnyPointerType()) {
99 | return "Pointer";
100 | }
101 | return 0;
102 | }
103 | void EmitVarDecl(VarDecl *VD, bool zeroInitialize=false) {
104 | // FIXME: We need to create an ArrayBuffer for everything (or do something
105 | // more clever with pointers to other objects, but I don't think that's
106 | // possible - you can't alias numbers in JS)
107 | QualType Ty = VD->getType();
108 | OS << VD->getName();
109 | OS << " = (function () { var $tmp = new ArrayBuffer("
110 | << Ctx->getTypeSize(Ty) / 8 << ");";
111 | EmitInitializer(VD->getInit(), Ty, 0);
112 | OS << " return $tmp; })()";
113 | }
114 | bool TraverseVarDecl(VarDecl *VD) {
115 | // For static globals, we emit fields in the statics object.
116 | //
117 | // Emit
118 | if (VD->getStorageClass() == SC_Static) {
119 | OS << "CStatics[\"" << path << "\"].";
120 | EmitVarDecl(VD, true);
121 | OS << ";\n";
122 | return true;
123 | }
124 |
125 | OS << "var ";
126 | EmitVarDecl(VD);
127 | OS << ";\n";
128 | return true;
129 | }
130 | bool TraverseDeclStmt(DeclStmt *D) {
131 | VarDecl *FirstDecl = cast(*(D->decl_begin()));
132 | // static is bound to an entire statement, so if it's on the first decl
133 | // then it will be on all of them
134 |
135 | // For static locals, we emit fields on the function object.
136 | if (FirstDecl->isStaticLocal()) {
137 | for (DeclStmt::decl_iterator i=D->decl_begin(),e=D->decl_end() ; i!=e ; ++i){
138 | VarDecl *VD = cast(*i);
139 | OS << "if (this." << VD->getName() << " == undefined)\n{\nthis.";
140 | EmitVarDecl(VD, true);
141 | OS << ";\n}";
142 | }
143 | return true;
144 | }
145 |
146 | // Everything else is a local or global variable declaration, so normal
147 | // JavaScript scoping can be applied.
148 | OS << "var ";
149 | for (DeclStmt::decl_iterator i=D->decl_begin(),e=D->decl_end() ; i!=e ; ++i){
150 | EmitVarDecl(cast(*i));
151 | if (i+1 != e)
152 | OS << ", ";
153 | }
154 | return true;
155 | }
156 |
157 | void EmitInitializer(Expr *Init, QualType FieldTy, uint64_t offset=0) {
158 | if (FieldTy->isArithmeticType()) {
159 | OS << "$tmp.set" << JSPrimitiveForType(FieldTy) << '(' << offset << ", ";
160 | if (Init)
161 | TraverseStmt(Init);
162 | else
163 | OS << '0';
164 | OS << ");";
165 | } else if (FieldTy->isAnyPointerType()) {
166 | OS << "$tmp.setPointer(" << offset << ", ";
167 | if (Init)
168 | TraverseStmt(Init);
169 | else
170 | OS << '0';
171 | OS << ");";
172 | } else if (InitListExpr *SubList = dyn_cast(Init)) {
173 | EmitInitList(SubList, offset);
174 | } else {
175 | fprintf(stderr, "Don't know how to handle initializer:\n");
176 | Init->dump();
177 | }
178 | }
179 |
180 | void EmitInitList(InitListExpr *E, uint64_t baseOffset) {
181 | const RecordType *RT = cast(cast(E->getType())->getNamedType());
182 | const RecordDecl *RD = RT->getDecl();
183 | const ASTRecordLayout &Layout = Ctx->getASTRecordLayout(RD);
184 | Expr **Init = E->getInits();
185 | // This is almost certainly broken for array initialisers
186 | for (RecordDecl::field_iterator i=RD->field_begin(),e=RD->field_end()
187 | ; i!=e ; ++i, ++Init) {
188 | // TODO: Bitfields (yuck!)
189 | uint64_t offset = baseOffset + Layout.getFieldOffset((*i)->getFieldIndex()) / 8;
190 | EmitInitializer(*Init, i->getType(), offset);
191 | }
192 | }
193 |
194 | bool TraverseInitListExpr(InitListExpr *E) {
195 | EmitInitList(E, 0);
196 | return true;
197 | }
198 |
199 | bool VisitStringLiteral(StringLiteral *SL) {
200 | OS << "makeCString(\"" << SL->getString() << "\")";
201 | return true;
202 | }
203 | bool TraverseObjCStringLiteral(ObjCStringLiteral *L) {
204 | StringLiteral *SL = L->getString();
205 | OS << "makeObjCString(\"" << SL->getString() << "\")";
206 | return true;
207 | }
208 | bool VisitCharacterLiteral(CharacterLiteral *L) {
209 | OS << L->getValue();
210 | return true;
211 | }
212 |
213 | bool VisitIntegerLiteral(IntegerLiteral *IL) {
214 | OS << IL->getValue();
215 | return true;
216 | }
217 | bool VisitDeclRefExpr(DeclRefExpr *E) {
218 | Decl *D = E->getDecl();
219 | // For enums, insert the constant value
220 | if (EnumConstantDecl *ED = dyn_cast(D)) {
221 | OS << ED->getInitVal();
222 | return true;
223 | }
224 |
225 | VarDecl *VD = dyn_cast(D);
226 | OS<< '(';
227 | if (VD && VD->isStaticLocal()) {
228 | OS << "this.";
229 | } else if (VD && VD->getStorageClass() == SC_Static) {
230 | OS << "CStatics[\"" << path << "\"].";
231 | }
232 | OS << E->getDecl()->getName();
233 | OS<< ')';
234 | return true;
235 | }
236 | bool TraverseBinAssign(BinaryOperator *Op) {
237 | Expr *RHS = Op->getRHS();
238 | OS << '(';
239 | TraverseStmt(Op->getLHS());
240 | // FIXME: Structure assignments (memcpy call)
241 | // Store the value at address 0 in the LHS.
242 | OS << ").set" << JSPrimitiveForType(Op->getLHS()->getType()) << "(0, ";
243 | BeginExprResultTruncation(RHS);
244 | TraverseStmt(RHS);
245 | EndExprResultTruncation(RHS);
246 | OS << ')';
247 | return true;
248 | }
249 | // These binary ops are the same in C and JS, so they can just be emitted as-is.
250 | bool TraverseBinAdd(BinaryOperator *Op) { return TraverseBinOp(Op); }
251 | bool TraverseBinAnd(BinaryOperator *Op) { return TraverseBinOp(Op); }
252 | bool TraverseBinComma(BinaryOperator *Op) { return TraverseBinOp(Op); }
253 | // FIXME: add Math.abs() around integer division
254 | bool TraverseBinDiv(BinaryOperator *Op) { return TraverseBinOp(Op); }
255 | bool TraverseBinEQ(BinaryOperator *Op) { return TraverseBinOp(Op); }
256 | bool TraverseBinGE(BinaryOperator *Op) { return TraverseBinOp(Op); }
257 | bool TraverseBinGT(BinaryOperator *Op) { return TraverseBinOp(Op); }
258 | bool TraverseBinLE(BinaryOperator *Op) { return TraverseBinOp(Op); }
259 | bool TraverseBinLT(BinaryOperator *Op) { return TraverseBinOp(Op); }
260 | bool TraverseBinLand(BinaryOperator *Op) { return TraverseBinOp(Op); }
261 | bool TraverseBinLor(BinaryOperator *Op) { return TraverseBinOp(Op); }
262 | bool TraverseBinMul(BinaryOperator *Op) { return TraverseBinOp(Op); }
263 | bool TraverseBinNE(BinaryOperator *Op) { return TraverseBinOp(Op); }
264 | bool TraverseBinOr(BinaryOperator *Op) { return TraverseBinOp(Op); }
265 | bool TraverseBinRem(BinaryOperator *Op) { return TraverseBinOp(Op); }
266 | bool TraverseBinShl(BinaryOperator *Op) { return TraverseBinOp(Op); }
267 | bool TraverseBinShr(BinaryOperator *Op) { return TraverseBinOp(Op); }
268 | bool TraverseBinSub(BinaryOperator *Op) { return TraverseBinOp(Op); }
269 | bool TraverseBinXor(BinaryOperator *Op) { return TraverseBinOp(Op); }
270 |
271 | void BeginExprResultTruncation(Expr *E) {
272 | QualType Ty = E->getType();
273 | if (Ty->isIntegerType()) {
274 | OS << "(";
275 | }
276 | if (Ty->isSignedIntegerType()) {
277 | OS << "(" << (1LL << (Ctx->getTypeSize(Ty)-1)) << "+(";
278 | }
279 | }
280 | void EndExprResultTruncation(Expr *E) {
281 | QualType Ty = E->getType();
282 | // For signed overflow, we need to wrap. For unsigned, the spec is
283 | // undefined, but lots of code expects us to wrap, so we'll do that too.
284 | // We could maybe turn that off, unless -wrapv is specified.
285 | if (Ty->isSignedIntegerType()) {
286 | OS << ")) % " << (1LL << Ctx->getTypeSize(Ty));
287 | OS << ")-" << (1LL << (Ctx->getTypeSize(Ty)-1));
288 | } else if (Ty->isIntegerType()) {
289 | OS << ") % " << (1LL << Ctx->getTypeSize(Ty));
290 | }
291 | }
292 |
293 | bool TraverseBinOp(BinaryOperator *Op) {
294 | if (Op->getType()->isAnyPointerType()) {
295 | OS << '(';
296 | TraverseStmt(Op->getLHS());
297 | int64_t offset = Ctx->getTypeSize(Op->getType()->getPointeeType())/8;
298 | if (0 != offset) {
299 | if (Op->getOpcode() == BO_Sub)
300 | offset = 0-offset;
301 | OS << ".pointerAdd(" << offset << " * ";
302 | TraverseStmt(Op->getRHS());
303 | OS << ')';
304 | }
305 | OS << ')';
306 | return true;
307 | }
308 | BeginExprResultTruncation(Op);
309 | TraverseStmt(Op->getLHS());
310 | OS << Op->getOpcodeStr();
311 | TraverseStmt(Op->getRHS());
312 | EndExprResultTruncation(Op);
313 | return true;
314 | }
315 | bool TraversePrefixOp(UnaryOperator *Op) {
316 | Expr *subExpr = Op->getSubExpr();
317 | OS << '(';
318 | TraverseStmt(subExpr);
319 | OS << ").set" << JSPrimitiveForType(subExpr->getType()) << "(0, ";
320 | BeginExprResultTruncation(subExpr);
321 | TraverseStmt(subExpr);
322 | OS << ".get" << JSPrimitiveForType(subExpr->getType()) << "(0)";
323 | switch (Op->getOpcode()) {
324 | default:
325 | assert(0 && "Not reached!");
326 | case UO_PreInc:
327 | TraverseStmt(Op->getSubExpr());
328 | OS << "+1";
329 | break;
330 | case UO_PreDec:
331 | TraverseStmt(Op->getSubExpr());
332 | OS << "-1";
333 | break;
334 | case UO_Plus:
335 | OS << "Math.abs(";
336 | TraverseStmt(Op->getSubExpr());
337 | OS << ')';
338 | break;
339 | case UO_Minus:
340 | OS << "0-Math.abs(";
341 | TraverseStmt(Op->getSubExpr());
342 | OS << ')';
343 | break;
344 | case UO_Not:
345 | OS << "~(";
346 | TraverseStmt(Op->getSubExpr());
347 | OS << ')';
348 | break;
349 | case UO_LNot:
350 | OS << "!(";
351 | TraverseStmt(Op->getSubExpr());
352 | OS << ')';
353 | break;
354 | }
355 | EndExprResultTruncation(Op);
356 | OS << "))";
357 | return true;
358 | }
359 | bool TraversePostfixOp(UnaryOperator *Op) {
360 | Expr *subExpr = Op->getSubExpr();
361 | OS << "(( function() { var $tmp = ";
362 | TraverseStmt(subExpr);
363 | OS << ".get" << JSPrimitiveForType(subExpr->getType()) << "(0);(";
364 | TraverseStmt(subExpr);
365 | OS << ").set" << JSPrimitiveForType(subExpr->getType()) << "(0, ";
366 | BeginExprResultTruncation(subExpr);
367 | if (Op->getOpcode() == UO_PostInc)
368 | OS << "$tmp+1";
369 | else
370 | OS << "$tmp-1";
371 | EndExprResultTruncation(Op);
372 | OS << ");";
373 | OS << " return $tmp; })())";
374 | return true;
375 | }
376 | bool TraverseUnaryPostInc(UnaryOperator *Op) { return TraversePostfixOp(Op); }
377 | bool TraverseUnaryPostDec(UnaryOperator *Op) { return TraversePostfixOp(Op); }
378 | bool TraverseUnaryPreInc(UnaryOperator *Op) { return TraversePrefixOp(Op); }
379 | bool TraverseUnaryPreDec(UnaryOperator *Op) { return TraversePrefixOp(Op); }
380 | bool TraverseUnaryPlus(UnaryOperator *Op) { return TraversePrefixOp(Op); }
381 | bool TraverseUnaryMinus(UnaryOperator *Op) { return TraversePrefixOp(Op); }
382 | bool TraverseUnaryNot(UnaryOperator *Op) { return TraversePrefixOp(Op); }
383 | bool TraverseUnaryLNot(UnaryOperator *Op) { return TraversePrefixOp(Op); }
384 |
385 | /**
386 | * Take the address of an object by creating an array containing that object.
387 | * Modifications to the object will then modify the original object.
388 | */
389 | bool TraverseUnaryAddrOf(UnaryOperator *Op) {
390 | OS << "new AddressOf(";
391 | TraverseStmt(Op->getSubExpr());
392 | OS << ')';
393 | return true;
394 | }
395 | bool TraverseUnaryDeref(UnaryOperator *Op) {
396 | OS << '(';
397 | TraverseStmt(Op->getSubExpr());
398 | OS << ").dereference()";
399 | return true;
400 | }
401 |
402 | bool TypesEquivalent(QualType a, QualType b) {
403 | if (Ctx->hasSameUnqualifiedType(a, b))
404 | return true;
405 | if (a->isPointerType() && b->isPointerType())
406 | return TypesEquivalent(a->getPointeeType(), b->getPointeeType());
407 | return false;
408 | }
409 |
410 | bool TraverseCastExpr(CastExpr *E) {
411 | // Most C cast expressions can be completely ignored in JS - they will
412 | // happen by magic at run time.
413 | Expr *Body = E->getSubExpr();
414 | switch (E->getCastKind()) {
415 | default:
416 | TraverseStmt(Body);
417 | break;
418 | case CK_ArrayToPointerDecay:
419 | OS << "(new AddressOf(";
420 | TraverseStmt(Body);
421 | OS << "))";
422 | break;
423 | case CK_LValueToRValue:
424 | TraverseStmt(Body);
425 | // If this is an l-value to r-value cast, then it's really a load
426 | // expression. Do that, except for direct references to variables,
427 | // which we can read directly.
428 | if (JSPrimitiveForType(E->getType())) {
429 | OS << ".get" << JSPrimitiveForType(E->getType()) << "(0)";
430 | }
431 | break;
432 | case CK_FloatingToIntegral:
433 | // Integers and floating point values in JS are the same, but we should
434 | // drop the fractional part.
435 | OS << "Math.floor(";
436 | TraverseStmt(Body);
437 | OS << ')';
438 | break;
439 | case CK_CPointerToObjCPointerCast:
440 | E->dump();
441 | llvm::errs() << "Casts to Objective-C object types are not supported.";
442 | return false;
443 | case CK_BitCast:
444 | TraverseStmt(Body);
445 | return true;
446 | E->dump();
447 | /*
448 | * Enum stuff:
449 | Expr::EvalResult Result;
450 | E->Evaluate(Result, CGF.getContext());
451 | OS << Result.Val.getInt();
452 | */
453 | }
454 | return true;
455 | }
456 | bool TraverseCStyleCastExpr(CStyleCastExpr *E) {
457 | return TraverseCastExpr(E);
458 | }
459 | bool TraverseImplicitCastExpr(ImplicitCastExpr *E) {
460 | return TraverseCastExpr(E);
461 | }
462 | bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E) {
463 | TraverseStmt(E->getBase());
464 | Expr *I = E->getIdx();
465 | // If it's a constant expression, evaluate it here!
466 | if (I->isEvaluatable(*Ctx) && !I->HasSideEffects(*Ctx)) {
467 | int64_t offset = ((Ctx->getTypeSize(E->getType())) / 8);
468 | llvm::APSInt result;
469 | I->EvaluateAsInt(result, *Ctx);
470 | offset *= result.getSExtValue();
471 | // If the offset is not 0, do some pointer arithmetic, otherwise this is
472 | // a null operation so do nothing.
473 | if (offset != 0) {
474 | OS << ".pointerAdd(" << offset << ')';
475 | }
476 | } else {
477 | OS << ".pointerAdd(";
478 | OS << Ctx->getTypeSize(E->getType())/8 << " * ";
479 | TraverseStmt(I);
480 | OS << ')';
481 | }
482 | OS << ".dereference()";
483 | return true;
484 | }
485 |
486 | bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
487 | // FIXME: VLA sizes
488 | llvm::APSInt result;
489 | E->EvaluateAsInt(result, *Ctx);
490 | OS << result.getSExtValue();
491 | return true;
492 | }
493 |
494 | bool TraverseCompoundStmt(CompoundStmt *S) {
495 | OS <<"{\n";
496 | for (CompoundStmt::body_iterator i=S->body_begin(),e=S->body_end() ; i!=e ;
497 | ++i){
498 | TraverseStmt(*i);
499 | OS << ";\n";
500 | }
501 | OS << "}\n";
502 | return true;
503 | }
504 | bool TraverseParenExpr(ParenExpr *E) {
505 | OS << "(";
506 | TraverseStmt(E->getSubExpr());
507 | OS << ")";
508 | return true;
509 | }
510 | bool TraverseForStmt(ForStmt *S) {
511 | OS << "for (";
512 | TraverseStmt(S->getInit());
513 | OS << " ; ";
514 | TraverseStmt(S->getCond());
515 | OS << " ; ";
516 | TraverseStmt(S->getInc());
517 | OS << ")\n";
518 | TraverseStmt(S->getBody());
519 | return true;
520 | }
521 | bool TraverseWhileStmt(WhileStmt *S) {
522 | OS << "while(";
523 | TraverseStmt(S->getCond());
524 | OS << ')';
525 | TraverseStmt(S->getBody());
526 | return true;
527 | }
528 | bool TraverseDoStmt(DoStmt *S) {
529 | OS << "do ";
530 | TraverseStmt(S->getBody());
531 | OS << "while(";
532 | TraverseStmt(S->getCond());
533 | OS << ')';
534 | return true;
535 | }
536 |
537 | bool TraverseCallExpr(CallExpr *E) {
538 | // If this is a direct call, then we call the function in the C 'namespace'
539 | // FIXME: Move static functions into a per-file namespace!
540 | if (FunctionDecl *FD = E->getDirectCallee()) {
541 | OS << "C." << FD->getName();
542 | } else {
543 | TraverseStmt(E->getCallee());
544 | }
545 | OS << '(';
546 | for (CallExpr::arg_iterator i=E->arg_begin(),e=E->arg_end() ; i!=e ; ++i) {
547 | if ((*i)->getType()->isStructureType()){
548 | OS << "cloneObject(";
549 | TraverseStmt(*i);
550 | OS << ")";
551 | } else {
552 | TraverseStmt(*i);
553 | }
554 | if (i+1 != e)
555 | OS << ", ";
556 | }
557 | OS << ')';
558 | return true;
559 | }
560 | bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
561 | // FIXME: super
562 | OS << "objc_msgSend(";
563 | if (E->isClassMessage())
564 | OS << "OBJC." <<
565 | E->getClassReceiver()->getAs()->getInterface()->getName();
566 | else
567 | TraverseStmt(E->getInstanceReceiver());
568 | OS << ", \"" << E->getSelector().getAsString() << '"';
569 | for (ObjCMessageExpr::arg_iterator i=E->arg_begin(),e=E->arg_end() ; i!=e ; ++i) {
570 | TraverseStmt(*i);
571 | if (i+1 != e)
572 | OS << ", ";
573 | }
574 | OS << ')';
575 | return true;
576 | }
577 | bool TraverseObjCMethodDecl(ObjCMethodDecl *OMD) {
578 | Stmt *body = OMD->getBody();
579 | // Declaration, ignore it.
580 | if (0 == body) return true;
581 |
582 | OS << "OBJC." << OMD->getClassInterface()->getName();
583 | if (OMD->isClassMethod())
584 | OS << ".isa";
585 | OS << ".methods[\"" << OMD->getSelector().getAsString();
586 | OS << "\"] = function(self, _cmd";
587 | for (ObjCMethodDecl::param_iterator i=OMD->param_begin(),e=OMD->param_end()
588 | ; i!=e ; ++i) {
589 | OS << ", ";
590 | TraverseDecl(*i);
591 | }
592 | OS << ")";
593 | TraverseStmt(body);
594 | OS << ";\n";
595 | return true;
596 | }
597 | bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
598 | // Skip dynamic properties
599 | if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) { return true;}
600 |
601 | ObjCPropertyDecl *OPD = D->getPropertyDecl();
602 | ObjCIvarDecl *Ivar = D->getPropertyIvarDecl();
603 | ObjCContainerDecl *cls = cast(Ivar->getDeclContext());
604 | OS << "OBJC." << cls->getName();
605 | OS << ".methods[\"" << OPD->getGetterName().getAsString();
606 | OS << "\"] = function(self, _cmd) { return self." << Ivar->getName();
607 | OS << "; };\n";
608 | if (!OPD->isReadOnly()) {
609 | OS << "OBJC." << cls->getName();
610 | OS << ".methods[\"" << OPD->getSetterName().getAsString();
611 | OS << "\"] = function(self, _cmd, newVal) { self." << Ivar->getName();
612 | OS << "= newVal; };\n";
613 | }
614 | return true;
615 | }
616 | const char* OpForAssignOp(BinaryOperatorKind Op) {
617 | switch (Op) {
618 | case BO_AddAssign: return "+";
619 | case BO_AndAssign: return "&";
620 | case BO_DivAssign: return "/";
621 | case BO_OrAssign: return "|";
622 | case BO_RemAssign: return "%";
623 | case BO_ShlAssign: return "<<";
624 | case BO_ShrAssign: return ">>";
625 | case BO_SubAssign: return "-";
626 | case BO_XorAssign: return "^";
627 | default: return "?";
628 | }
629 | }
630 | bool TraverseCompoundAssignOperator(CompoundAssignOperator *Op) {
631 | // FIXME: This is probably wrong, because we might be evaluating side
632 | // effects twice
633 | Expr *LHS = Op->getLHS();
634 | Expr *RHS = Op->getRHS();
635 | if (ObjCPropertyRefExpr *E = dyn_cast(LHS)) {
636 | OS << "objc_msgSend(";
637 | TraverseStmt(E->getBase());
638 | OS << ", \"" << E->getSetterSelector().getAsString() << "\", ";
639 | OS << "objc_msgSend(";
640 | TraverseStmt(E->getBase());
641 | OS << ", \"" << E->getGetterSelector().getAsString() << "\")";
642 | OS << OpForAssignOp(Op->getOpcode());
643 | TraverseStmt(RHS);
644 | OS << ')';
645 | } else {
646 | OS << '(';
647 | TraverseStmt(LHS);
648 | // Store the value at address 0 in the LHS.
649 | OS << ").set" << JSPrimitiveForType(LHS->getType()) << "(0, ";
650 | BeginExprResultTruncation(RHS);
651 | TraverseStmt(LHS);
652 | OS << ".get" << JSPrimitiveForType(LHS->getType()) << "(0)";
653 | OS << Op->getOpcodeStr()[0];
654 | TraverseStmt(RHS);
655 | EndExprResultTruncation(RHS);
656 | OS << ')';
657 | }
658 | return true;
659 | }
660 | bool TraverseBinAndAssign(CompoundAssignOperator *Op) {
661 | return TraverseCompoundAssignOperator(Op);
662 | }
663 | bool TraverseBinAddAssign(CompoundAssignOperator *Op) {
664 | return TraverseCompoundAssignOperator(Op);
665 | }
666 | bool TraverseBinDivAssign(CompoundAssignOperator *Op) {
667 | return TraverseCompoundAssignOperator(Op);
668 | }
669 | bool TraverseBinOrAddAssign(CompoundAssignOperator *Op) {
670 | return TraverseCompoundAssignOperator(Op);
671 | }
672 | bool TraverseBinRemAssign(CompoundAssignOperator *Op) {
673 | return TraverseCompoundAssignOperator(Op);
674 | }
675 | bool TraverseBinShlAssign(CompoundAssignOperator *Op) {
676 | return TraverseCompoundAssignOperator(Op);
677 | }
678 | bool TraverseBinShrAssign(CompoundAssignOperator *Op) {
679 | return TraverseCompoundAssignOperator(Op);
680 | }
681 | bool TraverseBinSubAssign(CompoundAssignOperator *Op) {
682 | return TraverseCompoundAssignOperator(Op);
683 | }
684 | bool TraverseBinXorAssign(CompoundAssignOperator *Op) {
685 | return TraverseCompoundAssignOperator(Op);
686 | }
687 |
688 | void WriteMemberExprBase(MemberExpr *E) {
689 | // If this is an arrow expression then we need to dereference it by getting
690 | // the pointer at address 0 in this memory view. The getPointer() function
691 | // will translate this offset back into the last value written.
692 | OS << '(';
693 | TraverseStmt(E->getBase());
694 | OS << ')';
695 | if (E->isArrow())
696 | OS << ".dereference()";
697 | }
698 |
699 | bool TraverseMemberExpr(MemberExpr *E) {
700 | Expr *Base = E->getBase();
701 | QualType BaseTy = Base->getType();
702 | const RecordType *StructTy = BaseTy->getAsStructureType();
703 | if (0 == StructTy) {
704 | StructTy = BaseTy->getAsUnionType();
705 | if (NULL == StructTy && BaseTy->isPointerType()) {
706 | QualType PointeeTy = BaseTy->getPointeeType();
707 | StructTy = PointeeTy->getAsStructureType();
708 | if (0 == StructTy)
709 | StructTy = BaseTy->getAsUnionType();
710 | }
711 | }
712 | const ASTRecordLayout &Layout = Ctx->getASTRecordLayout(StructTy->getDecl());
713 | // FIXME: Bitfields (yuck!)
714 | uint64_t offset =
715 | Layout.getFieldOffset(cast(E->getMemberDecl())->getFieldIndex()) / 8;
716 | QualType Ty = E->getType();
717 | // If this is an l-value expression then we just do the pointer arithmetic.
718 | if (E->isLValue()) {
719 | OS << '(';
720 | WriteMemberExprBase(E);
721 | OS << ").pointerAdd(" << offset << ')';
722 | return true;
723 | }
724 |
725 | // If the result is a pointer, then we need to read a pointer from the
726 | // buffer's pointer store, which is parallel to its data store.
727 | if (Ty->isPointerType()) {
728 | OS << "getPointer(";
729 | WriteMemberExprBase(E);
730 | OS << offset << ')';
731 | return true;
732 | }
733 | WriteMemberExprBase(E);
734 | // We now have two cases: A nested structure or a primitive. If it's a
735 | // structure, then we do some pointer arithmetic and return a new view on
736 | // this buffer. If it's a primitive, then we also do a load of that field
737 | if (Ty->isArithmeticType()) {
738 | OS << ".get" << JSPrimitiveForType(Ty) << '(' << offset << ')';
739 | return true;
740 | }
741 | return true;
742 | }
743 |
744 | bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
745 | OS << "objc_msgSend(";
746 | TraverseStmt(E->getBase());
747 | OS << ", \"" << E->getGetterSelector().getAsString() << "\")";
748 | return true;
749 | }
750 | bool VisitObjCImplDecl(ObjCImplDecl *D) {
751 | OS << "OBJC." << D->getName() << " = new ObjCClass(\"" << D->getName() << "\", ";
752 | if (ObjCInterfaceDecl *Super = D->getClassInterface()->getSuperClass())
753 | OS << '"' << Super->getName() << '"';
754 | else
755 | OS << "null";
756 | OS << ")\n";
757 | return true;
758 | }
759 | bool VisitObjCIvarDecl(ObjCIvarDecl *D) {
760 | ObjCContainerDecl *cls = cast(D->getDeclContext());
761 | OS << "OBJC." << cls->getName();
762 | OS << ".ivars.push(\"" << D->getName() << "\");\n";
763 | OS << "OBJC." << cls->getName();
764 | OS << ".template[\"" << D->getName() << "\"] = null;\n";
765 | return true;
766 | }
767 | bool TraverseReturnStmt(ReturnStmt *S) {
768 | OS << "return ";
769 | if (Expr *E = S->getRetValue()) {
770 | TraverseStmt(E);
771 | }
772 | return true;
773 | }
774 | /// Ignore enum constant declarations - we evaluate them as constants in the
775 | /// code
776 | bool TraverseEnumConstantDecl(EnumConstantDecl *D) { return true; }
777 | };
778 |
779 | class JavaScriptGeneratorAction : public PluginASTAction {
780 | protected:
781 | ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef inFile) {
782 | return new JSWriter(CI, inFile);
783 | }
784 |
785 | bool ParseArgs(const CompilerInstance &CI,
786 | const std::vector& args) {
787 | return true;
788 | }
789 | };
790 |
791 | }
792 |
793 | static FrontendPluginRegistry::Add
794 | X("-emit-js", "Writes the compilation unit out as JavaScript code");
795 |
--------------------------------------------------------------------------------