├── 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 | --------------------------------------------------------------------------------