23 Ekim 2013 Çarşamba

SpiderMonkey Javascript Motoru - Basit Bir Uygulama

Önceki yazıda bahsettiğimiz üç temel bileşenin her birinin gerektirdiği ilgili JSAPI fonksiyonlarını açıklayacak olursak:

  • Runtime: Runtime'ı oluşturmak için JS_NewRuntime, işimiz bittiğinde yok etmek için JS_DestroyRuntime kullanılır. Uygulamanın SpiderMonkey'le işi bittiğinde ayrılmış kaynakları tamamen bırakmak için  JS_ShutDown kullanılır.
  • Context: Context'i oluşturmak için JS_NewContext ve JS_DestroyContext kullanılır. ECMAScript standartlarına azami uyum için uygulamaların JSOPTION_VAROBJFIX'i JS_SetOptions'u kullanarak aktifleştirmeleri gerekir. En son Javascript dil özelliklerini almak için ise JS_SetVersion kullanılabilir. Ayrıca her context için hata raporlama JS_SetErrorReporter kullanılarak sağlanabilir.
  • Global Nesne: Bu nesneyi oluşturmadan önce JSCLASS_GLOBAL_FLAGS opsiyonuna sahip bir JSClass'a ihtiyacımız var. Aşağıda verilen örnekte metotları ve özellikleri olmayan çok basit bir JSClass (global_class isminde) tanımlanmıştır. Ardından global nesneyi oluşturmak için JS_NewGlobalObject'i kullanmamız gerekiyor. Daha sonra bu global nesnemizi standart javascript global nesneleriyle doldurmak için JS_InitStandardClasses kullanılır.

Bu kodlar basit bir uygulama için biraz kalabalık görünebilir. Aşağıda görüldüğü üzere kodlar 50 satır tutmakta. Ancak SpiderMonkey birden çok threadli, context'li ve global nesneli geniş ölçekli uygulamalar için de tasarlanmıştır. SpiderMonkey parçalarının çok farklı kombinasyonlarını destekleyen ve uygulamalara SpiderMonkey'in davranışları üzerinde hassas kontrol sunan ayrıntılı bir Apidir.

Aşağıda minimal bir JSAPI uygulaması için gereken kodlar sunulmuştur.

#include "jsapi.h"

/* global nesnenin sınıfı. */
static JSClass global_class = { "global",
                                JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
                                JS_PropertyStub,
                                JS_DeletePropertyStub,
                                JS_PropertyStub,
                                JS_StrictPropertyStub,
                                JS_EnumerateStub,
                                JS_ResolveStub,
                                JS_ConvertStub,
                                NULL,
                                JSCLASS_NO_OPTIONAL_MEMBERS
};

/* hata raporlayıcı metot. */
void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
     fprintf(stderr, "%s:%u:%s\n",
             report->filename ? report->filename : "[no filename]",
             (unsigned int) report->lineno,
             message);
}

int run(JSContext *cx) {
/* Context içinde herhangi bir şey çalıştırmadan önce bir istek girilir. */
    JSAutoRequest ar(cx);

    /* Yeni bir kompartman içinde global nesneyi oluşturuyoruz. */
    JSObject *global = JS_NewGlobalObject(cx, &global_class, NULL);
    if (global == NULL)
        return 1;

    /* Context'in global'ini set et */
    JSAutoCompartment ac(cx, global);
    JS_SetGlobalObject(cx, global);

    /* Global nesneyi Object ve Array gibi standart globallerle doldur. */
    if (!JS_InitStandardClasses(cx, global))
        return 1;

    /* Uygulama kodları burada olacak. Scriptleri çalıştırmak için ve kendi özel JS nesnelerinizi oluşturmak için JSAPI çağrılarını da bulundurabilir.  */
    return 0;
}

int main(int argc, const char *argv[]) {
    /* JS motorunu başlatıyoruz.  -- SpiderMonkey için gerekli. */
    if (!JS_Init())
       return 1;

    /* JS runtime'ı oluştur. */
    JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L, JS_NO_HELPER_THREADS);
    if (rt == NULL)
       return 1;

    /* Context'i oluştur. */
    JSContext *cx = JS_NewContext(rt, 8192);
    if (cx == NULL)
       return 1;
    JS_SetOptions(cx, JSOPTION_VAROBJFIX);
    JS_SetErrorReporter(cx, reportError);

    int status = run(cx);

    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);

    /* JS motorunu sonlandır. */
    JS_ShutDown();

    return status;
}

Her JSNative, Javascriptten beklediği argümanlara bakılmaksızın aynı imzaya sahiptir.

Fonksiyonlara argc ve vp.argc içinde verilen Javascript argümanları çağıran tarafından ne kadar esas argüman geçirildiğini söyler ve JS_ARGV(vx, vp) bu argümanların bir dizisini döndürür. Argümanların int ve float gibi yerli C++ tipleri yoktur bunlar daha ziyade Javascript tipleridir (jsval). Yerli fonksiyon argümanları C++ tiplerine çevirmek ve bunları yerel değişkenlerde saklamak için JS_ConvertArguments'i kullanır. Yerli fonksiyon Javascript dönüş değerlerini tutmak için JS_SET_RVAL(cx, vp, val)'i kullanır.

Başarılı olması durumunda JSNative, JS_SET_RVAL'i çağırmalı ve JS_TRUE döndürmelidir.

Hata olması durumunda JSNative bir hata raporlama fonksiyonu çağırır (JS_ReportError) ve JS_FALSE döndürür. Bu da Javascript istisnası fırlatılmasına neden olur. Çağıran bu istisnayı bir Javascript try/catch ifadesi yoluyla yakalayabilir.

Yerli fonksiyonları Javascript içerisinden çağrılabilir duruma getirmek için fonksiyonları tanımlayan JSFunctionSpec'lerin tablosu bildirilmelidir. Ardından JS_DefineFunctions çağrılır.

static JSFunctionSpec myjs_global_functions[] = {
    JS_FS("rand",   myjs_rand,   0, 0),
    JS_FS("srand",  myjs_srand,  0, 0),
    JS_FS("system", myjs_system, 1, 0),
    JS_FS_END
};

    ...
    if (!JS_DefineFunctions(cx, global, myjs_global_functions))
        return JS_FALSE;
    ...

Fonksiyonlar global içerisinde tanımlandıklarında bu global'i kullanan herhangi bir script bunları, tıpkı herhangi bir web sayfasının alert'i çağırabildiği gibi, global nesne olarak çağırabilir.

Bu ortam içinde biz de aşağıdaki gibi bir "merhaba dünya" scripti oluşturduk:

system("echo hello world");
 
 

Hiç yorum yok: