功能強大的JavaScript引擎–SpiderMonkey

楊粼波發表於2017-10-09

JavaScript是由Netscape開發的物件指令碼語言,其特點是開發簡單、功能靈活,目前已廣泛應用於WEB頁面及伺服器應用程式中。HTML本身是靜態的、不允許使用者干預,但用JavaScript編寫的指令碼程式就可以在使用者的瀏覽器端執行,可以同使用者進行互動,從而實現動態頁面。可以將JavaScript與嵌入WEB的大多數物件的事件(如滑鼠點選、移動等)相關聯,然後用自己的方式處理這些事件。JavaScript提供了豐富的內建函式及命令,能在瀏覽器中顯示HTML、數值計算、多媒體播放、超級連結以及簡單的互動視窗等,還可以使在瀏覽器中執行的小Java應用程式的性質改變從而很容易地改變控制元件或其它物件的行為而不必深入研究其本身的結構。

JavaScript雖然是為Internet而開發的,但它的應用卻不僅僅侷限於Internet,事實上由於其物件導向的特性使得其適用範圍非常廣泛,只要我們的應用程式具有良好的物件機制,我們就可以借用JavaScript,從而實現很好的互動功能。

SpiderMonkey是由C語言操作的JavaScript引擎,它支援JS1.4和ECMAScript-262規範。該引擎分析、編譯和執行指令碼,根據JS資料型別和物件的需要進行記憶體分配及釋放操作。利用該引擎可以讓你的應用程式具有解釋JavaScript指令碼的能力,目前已有若干個專案都採用了SpiderMonkey引擎,像K-3D、WebCrossing、WebMerger等。K-3D是用C++實現的3D建模與模擬系統,該系統內嵌SpiderMonkey引擎來提供自定義指令碼(使用者建立指令碼生成像齒輪一樣具有重複特性的複雜形體),也可用來驅動互動式的教學系統(使用者可以使用一段JS指令碼程式記錄其互動過程,如移動滑鼠、選擇選單、點選滑鼠等)。WebCrossing利用SpiderMonkey實現了伺服器端的指令碼環境,提供了完全的Web-server指令碼環境,伺服器端的實現允許你在內建的、物件導向的資料庫中建立永久物件,這樣即可根據自己的需要擴充套件伺服器環境。

下面簡要介紹在自己的應用程式中如何使用SpiderMonkey,最後給出一個簡單的例子程式。

如何使用引擎

JS引擎一般作為共享庫使用,應用程式呼叫引擎提供的API函式。引擎API函式大致分為以下幾種:資料型別操作、RunTime控制、類與物件的建立和維護、函式與指令碼執行、字串操作、錯誤處理、安全控制、Debug支援。 一般情況下,在你的應用程式中只需使用某幾類函式。例如,在進行JS呼叫之前你必須呼叫JS_NewRuntime函式來建立並初始化JS引擎。有些型別的函式,象安全控制類,提供可選擇的特徵。

從概念上講,JS引擎是你係統上的一個共享資源。通過將引擎API呼叫嵌入到應用程式中(包含jsapi.h檔案),你可以請求JS引擎進行操作。接下來,引擎處理你的請求,並將結果或狀態資訊返回給你的應用程式。

例如,假定你在使用JS引擎自動化應用程式,指令碼應用程式鑑別使用者並設定許可權。首先,應用程式建立JS物件,該物件描述使用者資訊,包括姓名、ID、許可權和可用的函式列表。在這種情況下,應用程式首先呼叫JS_NewObject建立物件。當JS引擎建立物件後,返回一個指標給應用程式。應用程式再呼叫JS引擎執行指令碼。在建立使用者物件後,應用程式即刻傳遞指令碼給JS_EvaluateScript以便編譯和執行。指令碼或許取得並效驗使用者資訊,然後建立使用者存取的權利。

JS引擎收到初始化請求後,給JS RunTime分配記憶體,應用程式使用的變數、物件和上下文(上下文)都儲存在RunTime中。一個上下文是指令碼的執行狀態(JS引擎使用的)。每個同時存在的指令碼或執行緒都必須有自己的上下文。單個的JS RunTime可以包含多個上下文、物件和變數。 幾乎所有的JS引擎呼叫都需要一個上下文變數,應用程式在建立RunTime後,首先應呼叫至少一次JS_NewContext來建立一個上下文。上下文的實際數量依賴於程式中同時使用的指令碼數。程式中每個同時存在的指令碼都需要一個上下文。另一方面,如果某個時刻只有一個指令碼編譯和執行,那麼你只需一個上下文給每個指令碼重複使用即可。

在建立上下文後,要呼叫JS_InitStandardClasses初始化引擎中的內建JS物件,包括Array、Boolean、Date、Math、Number和String。 即使在建立物件時傳遞一個特定的上下文給JS引擎,這個物件在RunTime中也是獨立於上下文。任意指令碼能與任意上下文建立聯絡以便存取任意物件。指令碼、上下文相互之間完全獨立,即使它們存取同樣的物件。在給定的RunTime中,應用程式能用未指定的上下文存取任意物件。你可以建立獨立的RunTime,一個用於共享上下文和物件,其餘的用於私有上下文和物件。但注意,某個時刻只有一個執行緒能存取特定的上下文。 要讓應用程式能識別JS,嵌入適當的引擎呼叫到你的程式中。大致有以下幾個方面:

  1. 程式中包含jsapi.h。
  2. 程式中提供結構和變數宣告。例如,如果你計劃傳遞一個指令碼給JS引擎,提供一個指令碼字串變數。用jsapi.h中定義的JS資料型別來宣告變數。
  3. 使用JavaScript的指令碼應用物件。通常這些物件與C程式中的結構和方法相對應。
  4. 將JS引擎API函式呼叫和變數引用插入到程式中,包括初始化內建JS物件、建立並配置使用者自定義物件。
  5. 大多數JS引擎呼叫返回一個值。如果該值是NULL,一般表示錯誤發生。如果非NULL,表示成功,返回值一般是指標,程式需要使用或留到將來使用。應用程式應檢查JS引擎呼叫的返回值。

要讓應用程式能解釋JavaScript,你必須遵循某些JS API嵌入習慣。下面的例子簡要說明需要嵌入到你的應用程式中去的一些API呼叫函式。大部分情況下,這些函式的插入順序是很重要的。例如,在呼叫其他JS API之前必須初始化JS RunTime,同樣在終止程式之前必須釋放JS RunTime。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 包含JS引擎的API標頭檔案 */
#include "jsapi.h"
.
.
.
//主程式宣告全域性JS變數,包括RunTime、一個Context和一個全域性物件,然後初始化JS RunTime、建立一個Context。
int main(int argc, char **argv)
{
  int c, i;
  /*宣告全域性JS變數,包括全域性和自定義物件*/
  JSVersion version;
  JSRuntime *rt;
  JSContext *cx;
  JSObject  *glob, *it;
  JSBool builtins;
  /* 初始化JS RunTime,返回結果給rt */
  rt = JS_NewRuntime(8L * 1024L * 1024L);
  /* 如果rt為空,程式終止 */
  if (!rt)
    return 1;
  /* 建立一個Context,並將其與JS RunTime關聯起來 */
  cx = JS_NewContext(rt, 8192);
  /* 如果cx為空,程式終止 */
  if (cx == NULL)
    return 1;
  /* 建立全域性物件 */
  glob = JS_NewObject(cx, clasp, NULL, NULL);
  /* 例項化內建物件和全域性物件*/
  builtins = JS_InitStandardClasses(cx, glob);
  .
  .
  .
  return 0;
}


如上面這個例子所示,呼叫JS引擎的應用程式必須首先建立JS RunTime,而且在終止程式之前要釋放這個RunTime。在例項化RunTime後,即可建立自己的JS物件模型。物件模型決定了JS物件之間的關係,JS物件本質上是一種層次結構。預設情況下,所有的JS物件都與全域性物件相關聯,它們都是全域性物件的後代。當初始化標準的JS類時,你自動地得到一個全域性物件:

builtins = JS_InitStandardClasses(cx, glob);

這個全域性物件建立了一些基本的、被其它物件所繼承的性質和方法。當你建立自定義物件時,它們自動使用全域性物件所定義的性質和方法。你可以在自定義物件上重新定義這些性質和方法,從而過載這些預設的性質和方法。當然,你也可以接受這些預設的分配。 你可以在內建JS物件或其它自定義物件的基礎上建立自己的物件。無論哪種情況,你所建立的物件都繼承了層次鏈中父物件、一直上溯到全域性物件的全部性質和方法。

管理RunTime

JS RunTime是記憶體空間,JS引擎利用它來管理上下文、物件和與JS函式及指令碼相關的變數。在執行JS函式或指令碼之前,首先要呼叫JS_NewRunTime來初始化一個RunTime。JS_NewRunTime函式攜帶一個unsigned整型引數,這個引數指定了在碎片收集之前分配給RunTime記憶體的最大位元組數。例如:

rt = JS_NewRuntime(8L * 1024L * 1024L);

如上所示,JS_NewRuntime返回一個指向RunTime的指標。非NULL表示建立成功。

正常情況下,一個程式只需一個RunTime。當然,根據需要建立多個RunTime並將它們儲存在不同指標上也是可以的。

JS_DestroyRuntime(rt);

如果你建立了多個RunTime,務必在應用程式終止前將每個都銷燬。

管理上下文(Contexts)

幾乎所有的JS API呼叫都要求你傳遞一個上下文引數。在JavaScript引擎中一個上下文代表一個指令碼,引擎傳遞上下文資訊給執行指令碼的執行緒。每個同時執行的指令碼必須指派一個唯一的上下文。當一個指令碼執行完後,它的上下文也不再有用,因此這個上下文可以重新指派給一個新的指令碼,或將其釋放。

呼叫函式JS_NewContext為某個指令碼建立一個新的上下文。這個函式需要兩個引數:一個與該上下文相關的RunTime指標,分配給該上下文的棧空間位元組數。如果呼叫成功,函式返回一個指標,它指向這個新建立的上下文。例如:

JSContext *cx;
      .
      .
      .
cx = JS_NewContext(rt, 8192);


這個RunTime必須已經存在。你指派給上下文的棧空間必須足夠大以便提供給使用該上下文的指令碼所建立的變數和物件。注意,因為需要一些與分配和維護上下文相關的overhead,你必須做到:在應用程式中必須根據需要來確定建立上下文的數量;要確保上下文在被應用程式所需要時存在,而不是反覆銷燬和需要時重新建立。

當某個上下文不再需要時,它應被銷燬、釋放記憶體資源給其它程式使用。根據應用程式中JS使用的範圍,可以在使用完後及時銷燬,或將其保留並反覆利用直到應用程式終止。無論哪種情況,當上下文不再需要時都要呼叫函式JS_DestroyContext來釋放它,這個函式攜帶一個指標引數,它指向要被釋放的上下文:

JS_DestroyContext(cx);

如果你的應用程式建立了多個RunTime,那麼,應用程式可能需要知道某個上下文是與哪個RunTime相關聯的。這種情況下,可以呼叫函式JS_GetRuntime,同時傳遞該上下文作為引數。JS_GetRuntime返回一個指標,它指向合適的RunTime(如果存在的話):

rt=JS_GetRuntime(cx);

當你建立一個上下文,你要給它指派棧空間用於存放變數和物件。在一個給定的上下文中,你也能夠存放大量的資料。但是,你必須將所需的棧空間儘可能地降到最小。呼叫JS_SetContextPrivate函式建立一個指標,它指向該上下文所需的私有資料,呼叫JS_GetContextPrivate函式得到這個指標以便你能存取這資料。你的應用程式負責建立和管理私有資料。

要建立私有資料並將其與上下文相關聯:首先,建立私有資料,即常規的C語言void* 變數;然後,呼叫JS_SetContextPrivate函式,並指定建立私有資料的上下文和指向該資料的指標。例如:

JS_SetContextPrivate(cx,pdata);

隨後要獲取這個資料指標,請呼叫JS_GetContextPrivate,並傳遞這個上下文作為引數。這個函式返回指向私有資料的指標:

pdata=JS_GetContextPrivate(cx);

<?xml encoding="US-ASCII"?>
<!ELEMENT order (header,item+,price)>
<!ELEMENT header (billing,shipping)>
<!ELEMENT billing (name,address,creditCard)>
<!ELEMENT shipping (name,address)>
<!ELEMENT name EMPTY>


物件的處理

1.建立內建物件和全域性JS物件

JavaScript引擎提供若干個內建物件,使得你的開發任務得以簡化。例如,內建陣列(Array)物件使得在JS引擎中建立和運算元組結構很容易。類似地,日期(Date)物件提供了一個操作日期的統一機制。要了解內建物件支援的全部內容,請參閱JS_InitStandardClasses。 JS引擎一直使用函式和全域性物件。通常,全域性物件居留在幕後,為應用程式中建立和使用的其它JS物件及全域性變數提供預設範圍。在建立自己的物件前,你必須初始化全域性物件。函式物件使得物件具有和呼叫建構函式的功能。

一個簡單的API呼叫,JS_InitStandardClasses,初始化全域性和函式物件、內建引擎物件,方便應用程式使用它們:

JSBool builtins;
      .
      .
      .
builtins = JS_InitStandardClasses(cx, glob);


JS_InitStandardClasses函式返回一個JS布林值,表示初始化成功與否。

你也可以為應用程式指定另外一個不同的全域性物件。例如,Navigator使用window作為其全域性物件。要改變應用程式的全域性物件,請呼叫JS_SetGlobalObject。要了解更多資訊,請參閱JS_SetGlobalObject。

2.建立並初始化自定義物件

除了使用引擎內建物件外,你還可以建立、初始化並使用自己的JS物件。特別是你在使用JS引擎用指令碼來自動化應用程式時更是如此。自定義的JS物件能提供直接的程式服務,或者作為你的程式服務的介面。

有兩種方法來建立JS引擎能使用的自定義物件:

  1. 寫一個JS指令碼,它建立一個物件、性質、方法、建構函式,然後將這個指令碼傳遞給JS引擎。
  2. 將程式碼插入到你的應用程式中,它定義了物件的性質和方法,呼叫引擎來初始化一個新物件,然後通過額外的引擎呼叫設定物件的性質。這種方法的好處是,應用程式能包含操作物件的本地方法。

無論哪種情況,如果你建立一個物件,然後讓其存在於被其它指令碼使用的RunTime中,你可以呼叫JS_AddRef和JS_AddNamedRoot使該物件為根。使用這些函式,確保JS引擎能跟蹤這些物件並在碎片收集時清除它們。

3.如何將自定義物件嵌入到應用程式中

將自定義物件插入到應用程式中是很有用的,比如,當物件持續需要時,或者你知道有多個指令碼需要使用一個物件。將自定義物件插入到應用程式中的步驟是:

  • 建立一個JSPropertySpec資料型別,將物件的屬性資訊指派給它,包括屬性的GET和PUT方法名字。
  • 建立一個JSFunctionSpec資料型別,將被你的物件所使用的方法資訊指派給它。
  • 建立實際的C函式,它們在響應你的物件方法呼叫時被執行。
  • 呼叫JS_NewObject和JS_ConstructObject函式,以便例項化該物件。
  • 呼叫JS_DefineFunctions函式來建立物件的方法。
  • 呼叫JS_DefineProperties函式來建立物件的屬性。

描述持續的、自定義的JS物件的程式碼必須放在靠近程式執行的開始部分,在那些依耐於先前已存在物件的程式碼之前。

4.給物件提供私有資料

象上下文一樣,你可以將大量的資料與物件進行關聯,而不是將這些資料直接存放在物件裡。呼叫JS_SetPrivate函式來建立指向物件私有資料的指標,呼叫JS_GetPrivate函式來獲取這個指標以便你能存取這些資料。你的應用程式負責建立和管理這些私有資料。

建立私有資料並將其與物件關聯的方法:

1)建立私有資料,作為C語言的void*變數。 2)呼叫JS_SetPrivate函式,指定物件和私有資料指標。

例如:

JS_SetContextPrivate(cx,obj,pdata);

隨後,要獲取這些資料,請呼叫JS_GetPrivate函式,將物件作為引數進行傳遞。這個函式返回指向物件私有資料的指標:

pdata=JS_GetContextPrivate(cx,obj);

資料處理

1.處理JS資料型別

JavaScript定義了自己的資料型別。有些資料型別直接對應於C語言中的副本。其它的,如JSObject、jsdouble和JSString,都是JavaScript獨有的。

通常,你可以在應用程式中像使用標準的C語言資料型別一樣宣告、使用JS資料型別,JS引擎對那些需要多於一個字儲存空間的JS資料型別的變數保持單獨的棧,例如:JSObject、jsdouble和JSString。引擎會週期性地檢查這些變數,看看它們是否仍在使用,如果沒有,引擎就碎片收集它們,釋放儲存空間。

2.處理JS值

除了JS資料型別以外,JS引擎也使用JS值,稱其為jsvals。一個jsval本質上是一個指標,指向除了整型以外的JS資料型別。對於整型,一個jsval包含這個值自身。其它情況,指標被編碼成包含額外資訊。利用jsvals提高引擎的效率,允許API函式處理大量的潛在資料型別。 引擎API包含一組巨集,用於測試一個jsval的JS資料型別。他們是:

  • JSVAL_IS_OBJECT
  • JSVAL_IS_NUMBER
  • JSVAL_IS_INT
  • JSVAL_IS_DOUBLE
  • JSVAL_IS_STRING
  • JSVAL_IS_BOOLEAN

除了測試一個jsval的潛在資料型別外,也能測試它看是否是原始JS資料型別(JSVAL_IS_PRIMITIVE)。原始資料型別是undefined、null、boolean、numeric和string型別。

你也可測試jsval指向的值是否為NULL(JSVAL_IS_NULL)或void(JSVAL_IS_VOID)。

如果一個jsval指向一個JSObject、 jsdouble或 jsstr等JS資料型別,你可利用JSVAL_TO_OBJECT、 JSVAL_TO_DOUBLE、 JSVAL_TO_STRING將jsval轉為它的潛在型別。

3.處理JS字串

你在JavaScript中做的許多事情都會涉及到字串,JS引擎實現了一個稱為JSString的字串資料型別和一個指向JS字元陣列的指標型別即jschar,用類處理Unicode編碼的字串。這個引擎也實現了一組通用的Unicode字串程式。最後,JS引擎也提供內建串的支援,兩個或多個獨立的字串在記憶體中能共享一個串。對於JSString型別的字串,這個引擎跟蹤並管理串資源。

一般說來,當你用JS引擎操縱字串時,你應該用JS API串處理函式來建立和複製字串。有字串管理程式用於建立NULL結尾的字串或者指定長度的字串。同時,也有程式用於計算字串長度、比較字串。

4.對Unicode和Interned字串的支援

像其他API呼叫一樣,具有Unicode能力的API字串函式的名字與標準的引擎API字串函式的名字是一一對應的。例如,如果一個標準函式名為JS_NewStringCopyN,對應的Unicode版函式就是JS_NewUCStringCopN。具有Unicode處理能力的API字串函式對於interned字串也是可行的。

為了節約空間,JS引擎為共享單個字串例項提供支援。這種共享的字串稱為”interned strings”。當你事先知道程式中會建立一個特定的、文字字串並且要多次使用它時,請利用interned字串。

引擎為interned字串提供了若干個呼叫:

  • JS_InternString,用於建立或再次使用一個JSString。
  • JS_InternUCString,用於建立或再次使用一個Unicode型別的JSString。
  • JS_InternUCStringN,用於建立或再次使用固定長度的Unicode型JSString。

5.安全控制

對於JavaScript1.3,JS引擎增加了安全增強型API函式,用於編譯和執行傳遞給引擎的指令碼或函式。JS安全模型是基於Java安全模型的。這個模型提供了一個通用的安全介面,但是,具體的安全實現是由應用程式自己來完成的。

安全機制用在能夠支援JavaScript的應用程式中的一種通用情形是比較指令碼的真實性或者限制指令碼的互動性。例如,你可以比較一個應用程式中兩個或多個指令碼的程式碼庫,只允許來自同一個程式碼庫的指令碼能夠修改共享程式碼庫的指令碼屬性。

如果要實現安全JS,請按以下步驟:

1)在程式中宣告一個或者多個JSPrincipals型別的結構。

2)實現將給陣列提供安全資訊的函式。這些函式包括:給你的應用程式提供一個principals陣列,用一套給定的規則對JS物件的引用數進行加減操作的機制。

3)用你的安全資訊給JSPrincipals結構賦值,這個資訊可以包括通用程式碼資訊。

4)在執行時間環境中,編譯與執行全部指令碼和函式。下面列出了這些API函式和它們的目的:

  • JS_CompileScriptForPrincipals:編譯但不執行一段具有安全能力的指令碼。
  • JS_CompileUCScriptForPrincipals:編譯但不執行一段具有安全能力、Unicode編碼的指令碼。
  • JS_CompileFunctionForPrincipals:利用一個文字字串建立一個具有安全能力的JS函式。
  • JS_CompileUCFunctionForPrincipals:利用一個Unicode編碼的文字字串建立一個具有安全資訊的JS函式。
  • JS_EvaluateScriptForPrincipals :編譯並執行一段具有安全能力的指令碼。
  • JS_EvaluateUCScriptForPrincipals:編譯並執行一段具有安全能力、用Unicode編碼的指令碼。

程式樣例

以下是一個簡單的樣例程式,它從檔案test.js中讀入一段指令碼,然後解釋執行並輸出結果。指令碼中可嵌入自定義物件People,People物件具有屬性name(表示該人的姓名)、address(表示該人的地址)及方法print(在螢幕上顯示該人的姓名、地址資訊)。

例如:下面是一段簡單的js指令碼,它首先利用print方法輸出該人的姓名和地址,然後將姓名和地址分別修改為John和Beijing,最後再次輸出其姓名和地址,看是否修改正確。

people.print();

people.name=”John”;

people.address=”Beijing”;

people.print();

下面是C源程式程式碼。

#include "js.h"
enum tagMY_PEOPLE {MY_NAME,MY_ADDRESS};
static JSBool GetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool SetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, 
        jsval *argv, jsval *rval);
typedef struct{
    char name[16];
    char addr[64];}PeopleInfo;
static PeopleInfo m_ainfo={"myName","myAddress"};
/*定義屬性的 GETTER*/
static JSBool GetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (JSVAL_IS_INT(id)) {
        switch (JSVAL_TO_INT(id)) {
            case MY_NAME:
                *vp=STRING_TO_JSVAL (JS_NewStringCopyZ (cx,m_ainfo.name));
                break;
            case MY_ADDRESS:
                *vp=STRING_TO_JSVAL (JS_NewStringCopyZ (cx,m_ainfo.addr));
                break;
            }
    }
    return JS_TRUE;
}
/*定義屬性的SETTER*/
static JSBool SetPeopleProperty (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    if (JSVAL_IS_INT(id)) {
        switch (JSVAL_TO_INT(id)) {
            case MY_NAME:
                strncpy (m_ainfo.name, JS_GetStringBytes (jss), 15);
                break;
            case MY_ADDRESS:
                strncpy (m_ainfo.addr, JS_GetStringBytes (jss), 63);
                break;
            }
    }
    return JS_TRUE;
}
/*定義print方法*/
static JSBool PeoplePrint(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, 
   jsval *rval)
{
    fprintf(stdout,"My Name is %s.
My Addr is %s.
",m_ainfo.name,m_ainfo.addr);
    return JS_TRUE;
}
void main()
{
    JSString* jss;
    char buf[5120];
    int len;
    jsval rval;
    JSRuntime *rt;
    JSContext *cx;
    JSObject *globalObj,*PeopleObj;
    JSClass global_class = {
            "global",0,
            JS_PropertyStub, JS_PropertyStub,JS_PropertyStub, JS_PropertyStub,
            JS_EnumerateStub, JS_ResolveStub,JS_ConvertStub, JS_FinalizeStub };
/*定義People類的屬性陣列*/
static JSPropertySpec PeopleProperties[] =
  {
    {"name", MY_NAME,    JSPROP_ENUMERATE },
    {"address",  MY_ADDRESS,     JSPROP_ENUMERATE },
    {0}
  } ;
/*定義People類的方法陣列*/
static JSFunctionSpec PeopleMethods[] =
{
    {"print",          PeoplePrint,     0},
             {0}
        };
/*定義People類*/
static JSClass PeopleClass = {
        "people",0,
  JS_PropertyStub,JS_PropertyStub, GetPeopleProperty, SetPeopleProperty,
  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
    typedef struct{}
      /* 初始化JS RunTime,返回結果給rt */
rt = JS_Init(1000000L);
    if (!rt)return;
  /* 建立一個上下文,並將其與JS RunTime關聯起來 */
    cx = JS_NewContext(rt, 5120);
    if (!cx)return;
  /* 建立全域性物件 */
    if (!(globalObj = JS_NewObject (cx, &global_class, NULL, NULL)))return;
      /* 例項化內建物件和全域性物件*/
JS_InitStandardClasses (cx, globalObj);
/*例項化People物件*/
PeopleObj = JS_DefineObject (cx, globalObj, "People", &PeopleClass, 0,JSPROP_ENUMERATE);
/*建立物件的屬性*/
JS_DefineProperties (cx,PeopleObj, PeopleProperties);
/*建立物件的方法*/
JS_DefineFunctions (cx,PeopleObj, PeopleMethods);
FILE* fp;
/*開啟檔案,讀入指令碼*/
    if (!(fp = fopen ("test.js", "r")))return;
    len = fread (buf, 1, 5120, fp);
    fclose (fp);
    if (len <= 0)return;
/*執行一段指令碼*/
    JS_EvaluateScript (cx, globalObj, buf, len, "", 1, &rval);
    jss = JS_ValueToString (cx, rval);
    fprintf(stdout,"The result is: %s",JS_GetStringBytes (jss));
/*釋放上下文*/
JS_DestroyContext(cx);
/*釋放RunTime*/
JS_DestroyRuntime(rt);
return;
}


相關文章