1/*
2 * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "JavaScriptGlue.h"
31#include "JSUtils.h"
32#include "JSBase.h"
33#include "JSObject.h"
34#include "JSRun.h"
35#include <JavaScriptCore/Completion.h>
36#include <JavaScriptCore/InitializeThreading.h>
37
38static CFTypeRef sJSCFNullRef = 0;
39
40static void CFJSObjectDispose(void *data);
41static JSObjectRef CFJSObjectCopyProperty(void *data, CFStringRef propertyName);
42static void CFJSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue);
43static CFTypeRef CFJSObjectCopyCFValue(void *data);
44static UInt8 CFJSObjectEqual(void *data1, void *data2);
45static CFArrayRef CFJSObjectCopyPropertyNames(void *data);
46
47void *JSCFRetain(CFAllocatorRef allocator, const void *value);
48void JSCFRelease(CFAllocatorRef allocator, const void *value);
49
50
51void JSSetCFNull(CFTypeRef nullRef)
52{
53    ReleaseCFType(sJSCFNullRef);
54    sJSCFNullRef = RetainCFType(nullRef);
55}
56
57CFTypeRef JSGetCFNull(void)
58{
59    return sJSCFNullRef;
60}
61
62/*
63    JSRetain
64*/
65JSTypeRef JSRetain(JSTypeRef ref)
66{
67    if (ref)
68    {
69        JSBase* ptr = (JSBase*)ref;
70        ptr->Retain();
71    }
72    return ref;
73}
74
75/*
76    JSRelease
77*/
78void JSRelease(JSTypeRef ref)
79{
80    if (ref)
81    {
82        JSBase* ptr = (JSBase*)ref;
83        ptr->Release();
84    }
85}
86
87/*
88    JSCopyDescription
89*/
90CFStringRef JSCopyDescription(JSTypeRef ref)
91{
92    CFStringRef result = 0;
93    if (ref)
94    {
95        JSBase* ptr = (JSBase*)ref;
96        ptr->CopyDescription();
97    }
98    return result;
99}
100
101/*
102    JSEqual
103*/
104UInt8 JSEqual(JSTypeRef ref1, JSTypeRef ref2)
105{
106    UInt8 result = false;
107    if (ref1 && ref2)
108    {
109        JSBase* ptr = (JSBase*)ref1;
110        result = ptr->Equal((JSBase*)ref2);
111    }
112    return result;
113}
114
115
116/*
117    JSGetTypeID
118*/
119JSTypeID JSGetTypeID(JSTypeRef ref)
120{
121    JSTypeID result = kJSInvalidTypeID;
122    if (ref)
123    {
124        JSBase* ptr = (JSBase*)ref;
125        result = ptr->GetTypeID();
126    }
127    return result;
128}
129
130
131/*
132    JSGetRetainCount
133*/
134CFIndex JSGetRetainCount(JSTypeRef ref)
135{
136    CFIndex result = -1;
137    if (ref)
138    {
139        JSBase* ptr = (JSBase*)ref;
140        result = ptr->RetainCount();
141    }
142    return result;
143}
144
145
146
147/*
148    JSObjectCreate
149*/
150JSObjectRef JSObjectCreate(void *data, JSObjectCallBacksPtr callBacks)
151{
152    JSObjectRef result = JSObjectCreateInternal(data, callBacks, 0, kJSUserObjectDataTypeUnknown);
153    return result;
154}
155
156/*
157    JSObjectCreateInternal
158*/
159JSObjectRef JSObjectCreateInternal(void *data, JSObjectCallBacksPtr callBacks, JSObjectMarkProcPtr markProc, int type)
160{
161    JSObjectRef result = 0;
162    JSUserObject* ptr = new JSUserObject(callBacks, markProc, data, type);
163    result = (JSObjectRef)ptr;
164    return result;
165}
166
167/*
168    JSObjectCopyCFValue
169*/
170CFTypeRef JSObjectCopyCFValue(JSObjectRef ref)
171{
172    CFTypeRef result = 0;
173    JSUserObject* ptr = (JSUserObject*)ref;
174    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
175    {
176        result = ptr->CopyCFValue();
177    }
178    return result;
179}
180
181/*
182    JSObjectGetData
183*/
184void *JSObjectGetData(JSObjectRef ref)
185{
186    void *result = 0;
187    JSUserObject* ptr = (JSUserObject*)ref;
188    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
189    {
190        result = ptr->GetData();
191    }
192    return result;
193}
194
195
196/*
197    JSObjectCopyProperty
198*/
199JSObjectRef JSObjectCopyProperty(JSObjectRef ref, CFStringRef propertyName)
200{
201    JSObjectRef result = 0;
202    JSUserObject* ptr = (JSUserObject*)ref;
203    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
204    {
205        result = (JSObjectRef)ptr->CopyProperty(propertyName);
206    }
207    return result;
208}
209
210
211/*
212    JSObjectSetProperty
213*/
214void JSObjectSetProperty(JSObjectRef ref, CFStringRef propertyName, JSObjectRef value)
215{
216    JSUserObject* ptr = (JSUserObject*)ref;
217    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
218    {
219        ptr->SetProperty(propertyName, (JSUserObject*)value);
220    }
221}
222
223
224/*
225    JSObjectCallFunction
226*/
227JSObjectRef JSObjectCallFunction(JSObjectRef ref, JSObjectRef thisObj, CFArrayRef args)
228{
229    JSObjectRef result = 0;
230    JSUserObject* ptr = (JSUserObject*)ref;
231    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
232    {
233        result = (JSObjectRef)ptr->CallFunction((JSUserObject*)thisObj, args);
234    }
235    return result;
236}
237
238
239/*
240    JSRunCreate
241*/
242JSRunRef JSRunCreate(CFStringRef jsSource, JSFlags inFlags)
243{
244    initializeThreading();
245
246    JSRunRef result = 0;
247    if (jsSource)
248    {
249        JSGlueAPIEntry entry;
250        result = (JSRunRef) new JSRun(jsSource, inFlags);
251    }
252    return result;
253}
254
255/*
256    JSRunCopySource
257*/
258CFStringRef JSRunCopySource(JSRunRef ref)
259{
260    CFStringRef result = 0;
261    JSRun* ptr = (JSRun*)ref;
262    if (ptr)
263    {
264        result = UStringToCFString(ptr->GetSource());
265    }
266    return result;
267}
268
269
270/*
271    JSRunCopyGlobalObject
272*/
273JSObjectRef JSRunCopyGlobalObject(JSRunRef ref)
274{
275    JSObjectRef result = 0;
276    JSRun* ptr = (JSRun*)ref;
277    if (ptr)
278    {
279        JSGlobalObject* globalObject = ptr->GlobalObject();
280        result = (JSObjectRef)KJSValueToJSObject(globalObject, globalObject->globalExec());
281    }
282    return result;
283}
284
285/*
286    JSRunEvaluate
287*/
288JSObjectRef JSRunEvaluate(JSRunRef ref)
289{
290    JSObjectRef result = 0;
291    JSRun* ptr = (JSRun*)ref;
292    if (ptr)
293    {
294        JSGlueAPIEntry entry;
295        Completion completion = ptr->Evaluate();
296        if (completion.isValueCompletion())
297        {
298            result = (JSObjectRef)KJSValueToJSObject(completion.value(), ptr->GlobalObject()->globalExec());
299        }
300
301        if (completion.complType() == Throw)
302        {
303            JSFlags flags = ptr->Flags();
304            if (flags & kJSFlagDebug)
305            {
306                CFTypeRef error = JSObjectCopyCFValue(result);
307                if (error)
308                {
309                    CFShow(error);
310                    CFRelease(error);
311                }
312            }
313        }
314    }
315    return result;
316}
317
318/*
319    JSRunCheckSyntax
320    Return true if no syntax error
321*/
322bool JSRunCheckSyntax(JSRunRef ref)
323{
324    bool result = false;
325    JSRun* ptr = (JSRun*)ref;
326    if (ptr)
327    {
328            JSGlueAPIEntry entry;
329            result = ptr->CheckSyntax();
330    }
331    return result;
332}
333
334/*
335    JSCollect - trigger garbage collection
336*/
337void JSCollect()
338{
339    initializeThreading();
340
341    JSGlueAPIEntry entry;
342    Heap* heap = getThreadGlobalExecState()->heap();
343    if (!heap->isBusy())
344        heap->collectAllGarbage();
345}
346
347/*
348    JSTypeGetCFArrayCallBacks
349*/
350void JSTypeGetCFArrayCallBacks(CFArrayCallBacks* outCallBacks)
351{
352    if (outCallBacks)
353    {
354        outCallBacks->version = 1;
355        outCallBacks->retain = (CFArrayRetainCallBack)JSCFRetain;
356        outCallBacks->release = (CFArrayReleaseCallBack)JSCFRelease;
357        outCallBacks->copyDescription = (CFArrayCopyDescriptionCallBack)JSCopyDescription;
358        outCallBacks->equal = (CFArrayEqualCallBack)JSEqual;
359    }
360}
361
362
363/*
364    JSCFRetain
365*/
366void *JSCFRetain(CFAllocatorRef allocator, const void *value)
367{
368    JSRetain((JSTypeRef)value);
369    return (void*)value;
370}
371
372/*
373    JSCFRelease
374*/
375void JSCFRelease(CFAllocatorRef allocator, const void *value)
376{
377    JSRelease((JSTypeRef)value);
378}
379
380
381/*
382    JSObjectCreateWithCFType
383*/
384JSObjectRef JSObjectCreateWithCFType(CFTypeRef inRef)
385{
386    JSObjectCallBacks callBacks;
387    JSObjectRef cfJSObject = nil;
388    if (inRef)
389    {
390        callBacks.dispose = CFJSObjectDispose;
391        callBacks.equal = CFJSObjectEqual;
392        callBacks.copyCFValue = CFJSObjectCopyCFValue;
393        callBacks.copyProperty = CFJSObjectCopyProperty;
394        callBacks.setProperty = CFJSObjectSetProperty;
395        callBacks.callFunction = 0;
396        callBacks.copyPropertyNames = CFJSObjectCopyPropertyNames;
397        cfJSObject = JSObjectCreateInternal((void*)CFRetain(inRef), &callBacks, 0, kJSUserObjectDataTypeCFType );
398    }
399    return cfJSObject;
400}
401
402/*
403    CFJSObjectDispose
404*/
405void CFJSObjectDispose(void *data)
406{
407    if (data)
408    {
409        CFRelease((JSTypeRef)data);
410    }
411}
412
413CFArrayRef JSObjectCopyPropertyNames(JSObjectRef ref)
414{
415    CFArrayRef result = 0;
416    JSUserObject* ptr = (JSUserObject*)ref;
417    if (ptr && (ptr->GetTypeID() == kJSObjectTypeID))
418    {
419        result = ptr->CopyPropertyNames();
420    }
421    return result;
422}
423/*
424    CFJSObjectCopyProperty
425*/
426JSObjectRef CFJSObjectCopyProperty(void *data, CFStringRef propertyName)
427{
428    JSObjectRef result = 0;
429    if (data && propertyName)
430    {
431        CFTypeRef cfResult = 0;
432        if (CFGetTypeID(data) == CFDictionaryGetTypeID())
433        {
434            if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo)
435            {
436                int len = CFDictionaryGetCount((CFDictionaryRef)data);
437                cfResult = CFNumberCreate(0, kCFNumberIntType, &len);
438            }
439            else
440            {
441                cfResult = RetainCFType(CFDictionaryGetValue((CFDictionaryRef)data, propertyName));
442            }
443        }
444        else if (CFGetTypeID(data) == CFArrayGetTypeID())
445        {
446            if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo)
447            {
448                int len = CFArrayGetCount((CFArrayRef)data);
449                cfResult = CFNumberCreate(0, kCFNumberIntType, &len);
450            }
451            else
452            {
453                SInt32 index = CFStringGetIntValue(propertyName);
454                CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data);
455                if (index >= 0 && index < arrayCount)
456                {
457                    cfResult = RetainCFType(CFArrayGetValueAtIndex((CFArrayRef)data, index));
458                }
459            }
460        }
461        else if (CFGetTypeID(data) == CFStringGetTypeID())
462        {
463            if (CFStringCompare(propertyName, CFSTR("length"), 0) == kCFCompareEqualTo)
464            {
465                int len = CFStringGetLength((CFStringRef)data);
466                cfResult = CFNumberCreate(0, kCFNumberIntType, &len);
467            }
468        }
469        if (cfResult)
470        {
471            result = JSObjectCreateWithCFType(cfResult);
472            CFRelease(cfResult);
473        }
474    }
475    return result;
476}
477
478
479/*
480    CFJSObjectSetProperty
481*/
482void CFJSObjectSetProperty(void *data, CFStringRef propertyName, JSObjectRef jsValue)
483{
484    if (data && propertyName)
485    {
486        CFTypeRef cfValue = JSObjectCopyCFValue(jsValue);
487
488        if (cfValue)
489        {
490            if (CFGetTypeID(data) == CFDictionaryGetTypeID())
491            {
492                CFDictionarySetValue((CFMutableDictionaryRef)data, propertyName, cfValue);
493            }
494            else if (CFGetTypeID(data) == CFArrayGetTypeID())
495            {
496                SInt32 index = CFStringGetIntValue(propertyName);
497                CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data);
498                if (index >= 0)
499                {
500                    for (; arrayCount < index; arrayCount++)
501                    {
502                        CFArrayAppendValue((CFMutableArrayRef)data, GetCFNull());
503                    }
504                    CFArraySetValueAtIndex((CFMutableArrayRef)data, index, cfValue);
505                }
506            }
507            CFRelease(cfValue);
508        }
509        else
510        {
511            if (CFGetTypeID(data) == CFDictionaryGetTypeID())
512            {
513                CFDictionaryRemoveValue((CFMutableDictionaryRef)data, propertyName);
514            }
515            else if (CFGetTypeID(data) == CFArrayGetTypeID())
516            {
517                SInt32 index = CFStringGetIntValue(propertyName);
518                CFIndex arrayCount = CFArrayGetCount((CFArrayRef)data);
519                if (index >= 0)
520                {
521                    for (; arrayCount < index; arrayCount++)
522                    {
523                        CFArrayAppendValue((CFMutableArrayRef)data, GetCFNull());
524                    }
525                    CFArraySetValueAtIndex((CFMutableArrayRef)data, index, GetCFNull());
526                }
527            }
528        }
529    }
530}
531
532
533/*
534    CFJSObjectCopyCFValue
535*/
536CFTypeRef CFJSObjectCopyCFValue(void *data)
537{
538    CFTypeRef result = 0;
539    if (data)
540    {
541        result = (CFTypeRef)CFRetain(data);
542    }
543    return result;
544}
545
546/*
547    CFJSObjectCopyCFValue
548*/
549UInt8 CFJSObjectEqual(void *data1, void *data2)
550{
551    UInt8 result = false;
552    if (data1 && data2)
553    {
554        CFEqual((CFTypeRef)data1, (CFTypeRef)data2);
555    }
556    return result;
557}
558
559
560/*
561    CFJSObjectCopyPropertyNames
562*/
563CFArrayRef CFJSObjectCopyPropertyNames(void *data)
564{
565    CFMutableArrayRef result = 0;
566    if (data)
567    {
568        CFTypeID cfType = CFGetTypeID(data);
569        if (cfType == CFDictionaryGetTypeID())
570        {
571            CFIndex count = CFDictionaryGetCount((CFDictionaryRef)data);
572            if (count)
573            {
574                CFTypeRef* keys = (CFTypeRef*)malloc(sizeof(CFTypeRef)*count);
575                if (keys)
576                {
577                    int i;
578                    CFDictionaryGetKeysAndValues((CFDictionaryRef)data, (const void **)keys, 0);
579                    for (i = 0; i < count; i++)
580                    {
581                        CFStringRef key = (CFStringRef)keys[i];
582                        if (CFGetTypeID(key) != CFStringGetTypeID()) continue;
583
584                        if (!result) result = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
585                        if (!result) continue;
586
587                        CFArrayAppendValue(result, key);
588                    }
589                    free(keys);
590                }
591            }
592        }
593    }
594    return result;
595}
596
597
598
599
600CFMutableArrayRef JSCreateCFArrayFromJSArray(CFArrayRef array)
601{
602    CFIndex count = array ? CFArrayGetCount(array) : 0;
603    CFMutableArrayRef cfArray = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
604    CFIndex i;
605
606    for (i = 0; cfArray && i <  count; i++)
607    {
608        JSObjectRef jsValue = (JSObjectRef)CFArrayGetValueAtIndex(array, i);
609        CFTypeRef cfvalue = JSObjectCopyCFValue(jsValue);
610        if (cfvalue)
611        {
612            CFArrayAppendValue(cfArray, cfvalue);
613            CFRelease(cfvalue);
614        }
615        else
616        {
617            CFArrayAppendValue(cfArray, GetCFNull());
618        }
619    }
620    return cfArray;
621}
622
623CFMutableArrayRef JSCreateJSArrayFromCFArray(CFArrayRef array)
624{
625    initializeThreading();
626
627    CFIndex count = array ? CFArrayGetCount(array) : 0;
628    CFArrayCallBacks arrayCallbacks;
629    CFMutableArrayRef jsArray;
630    CFIndex i;
631
632    JSTypeGetCFArrayCallBacks(&arrayCallbacks);
633    jsArray = CFArrayCreateMutable(0, 0, &arrayCallbacks);
634
635    for (i = 0; array && i <  count; i++)
636    {
637        CFTypeRef cfValue = (CFTypeRef)CFArrayGetValueAtIndex(array, i);
638        JSObjectRef jsValue = JSObjectCreateWithCFType(cfValue);
639
640        if (!jsValue) jsValue = JSObjectCreateWithCFType(GetCFNull());
641        if (jsValue)
642        {
643            CFArrayAppendValue(jsArray, jsValue);
644            JSRelease(jsValue);
645        }
646    }
647    return jsArray;
648}
649
650
651void JSLockInterpreter()
652{
653    initializeThreading();
654    JSLock::lock(LockForReal);
655}
656
657
658void JSUnlockInterpreter()
659{
660    JSLock::unlock(LockForReal);
661}
662