--- title: JavaScript C Engine Embedder's Guide slug: orphaned/JavaScript_C_Engine_Embedder's_Guide tags: - SpiderMonkey original_slug: JavaScript_C_Engine_Embedder's_Guide ---
이 문서는 C로 구현된 JavaScript 엔진인 SpiderMonkey를 전반적으로 소개하며, 애플리케이션이 JavaScript를 인식하도록 하기 위해 어떻게 엔진을 내장하는지에 대해 설명합니다. JavaScript엔진을 여러분의 애플리케이션에 내장하는데는 두가지 큰 이유가 있을 것입니다. 먼저, 스크립트를 사용하여 애플리케이션을 자동화하는 것이 있고, JavaScript엔진과 스크립트를 사용하여 특정 플랫폼에 의존적인 애플케이션 솔루션이 되지 않도록 크로스 플랫폼을 지원하는 것입니다.
모질라 JS 엔진은 JS1.8, JS 1.3을 통해 JS1.0을 지원하고 ECMAScript-262 규약을 충족합니다. 간단하게 보면, JS 엔진은 JS 문장과 함수를 포함하는 스크립트를 파싱하여 컴파일하고 실행합니다. 해당 엔진은 스크립트 실행에 필요한 JS 개체의 메모리 할당을 관리하고 더 이상 필요하지 않을 경우, 가비지 컬렉션을 통해 개체를 지웁니다.
일반적으로 JS엔진은 공유 리소스로 빌드합니다. 예를 들어, 윈도와 윈도NT에서는 DLL이고 유닉스에서는 공유 라이브러리가 됩니다. 사용방법은 간단합니다. 우선 애플리케이션에 JS엔진을 링크하고 나서 JS엔진 API를 애플리케이션에 임베딩합니다. 이렇게 하면 JS엔진 API는 다음 종류에 해당하는 기능을 제공하게 됩니다.
애플리케이션이 자바스크립트 호출을 지원하려면 런타임 제어(runtime control)와 데이터 타입 조정(datatype manipulation)등과 같은 종류의 기능을 사용합니다. 예를 들어, 어떤 자바스크립트를 호출하기전에 JS_NewRuntime
함수를 호출해서 JS엔진을 생성하고 초기화해야 하며, 보안 제어(security control)와 같은 기능은 필요에 따라 선택적으로 사용할 수 있습니다.
개념적으로 JS엔진은 시스템에 존재하는 공유 리소스라고 볼 수 있습니다. API call을 애플리케이션에 임베딩하여, JS엔진에 요청을 넘길 수 있습니다. 요청을 처리한 엔진은 값이나 상태정보를 애플리케이션에 되돌려줍니다. 그림1.1은 이러한 일반적인 관계를 보여주고 있습니다.
그림 1.1
예를 들어 여러분이 자바스크립트를 사용하여 애플리케이션을 자동화하기 위해 JS엔진을 사용한다고 가정합시다. 그리고 해당 애플리케이션에서 실행하는 스크립트에 사용자를 인증하고 애플리케이션에 사용자 접근 권한을 설정한다고 합시다. 우선, 애플리케이션은 사용자에게 이름, 아이디, 접근권한을 위한 입력과 사용자에게 허락되어 애플리케이션에서 사용 가능한 기능 리스트를 보여줄 수 있는 JS 오브젝트를 생성할 수도 있습니다.
이러한 경우, 애플리케이션이 처음으로 JS엔진에 요청하는 것은 JS_NewObject
를 호출하여 사용자 정의 개체를 생성하는 것입니다. JS엔진이 개체를 생성할 때, 애플리케이션에 해당 포인터를 반환합니다. 이때 부터 애플리케이션은 해당 개체를 사용하여 스크립트를 실행하기 위해 다시 JS엔진을 호출할 수 있습니다. 예를 들어, 사용자 개체를 생성한 이후, 애플리케이션은 스크립트를 즉시 컴파일하고 실행하기 위해 바로 JS_EvaluateScript
에 전달할 것입니다. 그 결과, 좀 전의 스크립트는 사용자 정보를 얻어 확인해주고 애플리케이션이 가진 다른 기능을 사용할 수 있도록 접근 권한을 허락하게 됩니다.
In truth, the actual relationship between your application and the JS engine is somewhat more complex than shown in Figure 1.1. For example, it assumes that you have already built the JS engine for your platform. It assumes that your application code includes jsapi.h
, and it assumes that the first call your application makes to the engine initializes the JS run time.
When the JS engine receives an initialization request, it allocates memory for the JS run time. Figure 1.2 illustrates this process:
Figure 1.2
The run time is the space in which the variables, objects, and contexts used by your application are maintained. A context is the script execution state for a thread used by the JS engine. Each simultaneously existent script or thread must have its own context. A single JS run time may contain many contexts, objects, and variables.
Almost all JS engine calls require a context argument, so one of the first things your application must do after creating the run time is call JS_NewContext
at least once to create a context. The actual number of contexts you need depends on the number of scripts you expect to use at the same time in your application. You need one context for each simultaneously existing script in your application. On the other hand, if only one script at a time is compiled and executed by your application, then you need only create a single context that you can then reuse for each script.
After you create contexts, you will usually want to initialize the built-in JS objects in the engine by calling JS_InitStandardClasses
. The built-in objects include the Array
, Boolean
, Date
, Math
, Number
, and String
objects used in most scripts.
Most applications will also use custom JS objects. These objects are specific to the needs of your applications. They usually represent data structures and methods used to automate parts of your application. To create a custom object, you populate a JS class for the object, call JS_InitClass
to set up the class in the run time, and then call JS_NewObject
to create an instance of your custom object in the engine. Finally, if your object has properties, you may need to set the default values for them by calling JS_SetProperty
for each property.
Even though you pass a specific context to the JS engine when you create an object, an object then exists in the run time independent of the context. Any script can be associated with any context to access any object. Figure 1.3 illustrates the relationship of scripts to the run time, contexts, and objects.
Figure 1.3
As Figure 1.3 also illustrates, scripts and contexts exist completely independent from one another even though they can access the same objects. Within a given run time, an application can always use any unassigned context to access any object. There may be times when you want to ensure that certain contexts and objects are reserved for exclusive use. In these cases, create separate run times for your application: one for shared contexts and objects, and one (or more, depending on your application's needs) for private contexts and objects.
NOTE: Only one thread at a time should be given access to a specific context.
Before you can use JS in your applications, you must build the JS engine as a shareable library. In most cases, the engine code ships with make files to automate the build process.
For example, under Unix, the js source directory contains a base gnu make file called Makefile.ref
, and a config
directory. The config
directory contains platform-specific .mk
files to use with Makefile.ref
for your environment. Under Windows NT the nmake file is js.mak
.
Always check the source directory for any readme
files that may contain late-breaking or updated compilation instructions or information.
To make your application JS-aware, embed the appropriate engine calls in your application code. There are at least five steps to embedding:
#include "jsapi.h"
to your C modules to ensure that the compiler knows about possible engine calls. Specialized JS engine work may rarely require you to include additional header files from the JS source code. For example, to include JS debugger calls in your application, code you will need to include jsdbgapi.h
in the appropriate modules. Most other header files in the JS source code should
not
be included. To do so might introduce dependencies based on internal engine implementations that might change from release to release.jsapi.h
.NULL
, it usually indicates an error condition. If the value is nonzero, it usually indicates success; in these cases, the return value is often a pointer that your application needs to use or store for future reference. At the very least, your applications should always check the return values from JS engine calls.The following code fragment illustrates most of these embedding steps, except for the creation of JS scripts, which lies outside the scope of the introductory text. For more information about creating scripts and objects using the JavaScript language itself, see the Client-Side JavaScript Guide . For further information about scripting server-side objects, see the Server-Side JavaScript Guide .
. . . #include <stdio.h> #include <stdlib.h> #include <string.h> /* include the JS engine API header */ #include "jsapi.h" . . . /* main function sets up global JS variables, including run time, * a context, and a global object, then initializes the JS run time, * and creates a context. */ int main(int argc, char **argv) { int c, i; /*set up global JS variables, including global and custom objects */ JSVersion version; JSRuntime *rt; JSContext *cx; JSObject *glob, *it; JSBool builtins; /* initialize the JS run time, and return result in rt */ rt = JS_NewRuntime(8L * 1024L * 1024L); /* if rt does not have a value, end the program here */ if (!rt) return 1; /* create a context and associate it with the JS run time */ cx = JS_NewContext(rt, 8192); /* if cx does not have a value, end the program here */ if (cx == NULL) return 1; /* create the global object here */ glob = JS_NewObject(cx, clasp, NULL, NULL); /* initialize the built-in JS objects and the global object */ builtins = JS_InitStandardClasses(cx, glob); . . . return 0; }
This example code is simplified to illustrate the key elements necessary to embed JS engine calls in your applications. For a more complete example -- from which these snippets were adapted -- see js.c
, the sample application source code that is included with the JS engine source code.
For most of the JavaScript aware applications you create, you will want to follow some standard JS API embedding practices. The following sections describe the types of API calls you need to embed in all your applications.
In many cases, the order in which you embed certain API calls is important to successful embedding. For example, you must initialize a JS run time before you can make other JS calls. Similarly, you should free the JS run time before you close your application. Therefore, your application's main function typically sandwiches API calls for initializing and freeing the JS run time around whatever other functionality you provide:
int main(int argc, char **argv) { int c, i; /*set up global JS variables, including global and custom objects */ JSVersion version; JSRuntime *rt; JSContext *cx; JSObject *glob, *it; . . . /* initialize the JS run time, and return result in rt */ rt = JS_NewRuntime(8L * 1024L * 1024L); /* if rt does not have a value, end the program here */ if (!rt) return 1; . . . /* establish a context */ cx = JS_NewContext(rt, 8192); /* if cx does not have a value, end the program here */ if (cx == NULL) return 1; /* initialize the built-in JS objects and the global object */ builtins = JS_InitStandardClasses(cx, glob); . . . /* include your application code here, including JS API calls * that may include creating your own custom JS objects. The JS * object model starts here. */ . . . /* Before exiting the application, free the JS run time */ JS_DestroyRuntime(rt);
As this example illustrates, applications that embed calls to the JS engine are responsible for setting up the JS run time as one of its first acts, and they are responsible for freeing the run time before they exit. In general, the best place to ensure that the run time is initialized and freed is by embedding the necessary calls in whatever module you use as the central JS dispatcher in your application.
After you initialize the run time, you can establish your application's JS object model. The object model determines how your JS objects relate to one another. JS objects are hierarchical in nature. All JS objects are related to the global object by default. They are descendants of the global object. You automatically get a global object when you initialize the standard JS classes:
builtins = JS_InitStandardClasses(cx, glob);
The global object sets up some basic properties and methods that are inherited by all other objects. When you create your own custom objects, they automatically use the properties and methods defined on the global object. You can override these default properties and methods by defining them again on your custom object, or you can accept the default assignments.
You can also create custom objects that are based on other built-in JS objects, or that are based on other custom objects. In each case, the object you create inherits all of the properties and methods of its predecessors in the hierarchical chain, all the way up to the global object. For more information about global and custom objects, see Initializing Built-in and Global JS Objects and Creating and Initializing Custom Objects.
The JS run time is the memory space the JS engine uses to manage the contexts, objects, and variables associated with JS functions and scripts. Before you can execute any JS functions or scripts you must first initialize a run time. The API call that initializes the run time is JS_NewRuntime
. JS_NewRuntime
takes a single argument, an unsigned integer that specifies the maximum number of bytes of memory to allocate to the run time before garbage collection occurs. For example:
rt = JS_NewRuntime(8L * 1024L * 1024L);
As this example illustrates, JS_NewRuntime
also returns a single value, a pointer to the run time it creates. A non-NULL return value indicates successful creation of the run time.
Normally, you only need one run time for an application. It is possible, however, to create multiple run times by calling JS_NewRuntime
as necessary and storing the return value in a different pointer.
When the JS run time is no longer needed, it should be destroyed to free its memory resources for other application uses. Depending on the scope of JS use in your application, you may choose to destroy the run time immediately after its use, or, more likely, you may choose to keep the run time available until your application is ready to terminate. In either case, use the JS_DestroyRuntime
to free the run time when it is no longer needed. This function takes a single argument, the pointer to the run time to destroy:
JS_DestroyRuntime(rt);
If you use multiple run times, be sure to free each of them before ending your application.
Almost all JS API calls require you to pass a context as an argument. A context identifies a script in the JavaScript engine. The engine passes context information to the thread that runs the script. Each simultaneously-executing script must be assigned a unique context. When a script completes execution, its context is no longer in use, so the context can be reassigned to a new script, or it can be freed.
To create a new context for a script, use JS_NewContext
. This function takes two arguments: a pointer to the run time with which to associate this context, and the number of bytes of stack space to allocate for the context. If successful, the function returns a pointer to the newly established context. For example:
JSContext *cx; . . . cx = JS_NewContext(rt, 8192);
The run time must already exist. The stack size you specify for the context should be large enough to accommodate any variables or objects created by the script that uses the context. Note that because there is a certain amount of overhead associated with allocating and maintaining contexts you will want to:
When a context is no longer needed, it should be destroyed to free its memory resources for other application uses. Depending on the scope of JS use in your application, you may choose to destroy the context immediately after its use, or, more likely, you may choose to keep the context available for reuse until your application is ready to terminate. In either case, use the JS_DestroyContext
to free the context when it is no longer needed. This function takes a single argument, the pointer to the context to destroy:
JS_DestroyContext(cx);
If your application creates multiple run times, the application may need to know which run time a context is associated with. In this case, call JS_GetRuntime
, and pass the context as an argument. JS_GetRuntime
returns a pointer to the appropriate run time if there is one:
rt = JS_GetRuntime(cx);
When you create a context, you assign it stack space for the variables and objects that get created by scripts that use the context. You can also store large amounts of data for use with a given context, yet minimize the amount of stack space you need. Call JS_SetContextPrivate
to establish a pointer to private data for use with the context, and call JS_GetContextPrivate
to retrieve the pointer so that you can access the data. Your application is responsible for creating and managing this optional private data.
To create private data and associate it with a context:
JS_SetContextPrivate
, and specify the context for which to establish private data, and specify the pointer to the data.For example:
JS_SetContextPrivate(cx, pdata);
To retrieve the data at a later time, call JS_GetContextPrivate
, and pass the context as an argument. This function returns the pointer to the private data:
pdata = JS_GetContextPrivate(cx);
The JavaScript engine provides several built-in objects that simplify some of your development tasks. For example, the built-in Array
object makes it easy for you to create and manipulate array structures in the JS engine. Similarly, the Date object provides a uniform mechanism for working with and handling dates. For a complete list of built-in objects supported in the engine, see the reference entry for JS_InitStandardClasses
.
The JS engine always uses function and global objects. In general, the global object resides behind the scenes, providing a default scope for all other JS objects and global variables you create and use in your applications. Before you can create your own objects, you will want to initialize the global object. The function object enables objects to have and call constructors.
A single API call, JS_InitStandardClasses
, initializes the global and function objects and the built-in engine objects so that your application can use them:
JSBool builtins; . . . builtins = JS_InitStandardClasses(cx, glob);
JS_InitStandardClasses
returns a JS boolean value that indicates the success or failure of the initialization.
You can specify a different global object for your application. For example, the Netscape Navigator uses its own global object, window. To change the global object for you application, call JS_SetGlobalObject
. For more information, see the reference entry for JS_SetGlobalObject
.
In addition to using the engine's built-in objects, you will create, initialize, and use your own JS objects. This is especially true if you are using the JS engine with scripts to automate your application. Custom JS objects can provide direct program services, or they can serve as interfaces to your program's services. For example, a custom JS object that provides direct service might be one that handles all of an application's network access, or might serve as an intermediary broker of database services. Or a JS object that mirrors data and functions that already exist in the application may provide an object-oriented interface to C code that is not otherwise, strictly-speaking, object-oriented itself. Such a custom object acts as an interface to the application itself, passing values from the application to the user, and receiving and processing user input before returning it to the application. Such an object might also be used to provide access control to the underlying functions of the application.
There are two ways to create custom objects that the JS engine can use:
In either case, if you create an object and then want it to persist in the run time where it can be used by other scripts, you must root the object by calling JS_AddRoot
or JS_AddNamedRoot
. Using these functions ensures that the JS engine will keep track of the objects and clean them up during garbage collection, if appropriate.
One reason to create a custom JS object from a script is when you only need an object to exist as long as the script that uses it is executing. To create objects that persist across script calls, you can embed the object code in your application instead of using a script.
NOTE: You can also use scripts to create persistent objects, too.
To create a custom object using a script:
JS_EvaluateScript
, JS_EvaluateUCScript
or 2.) compile the script once with a call to JS_CompileScript
or JS_CompileUCScript
, and then execute it repeatedly with individual calls to JS_ExecuteScript
. The "UC" versions of these calls provide support for Unicode-encoded scripts.An object you create using a script only can be made available only during the lifetime of the script, or can be created to persist after the script completes execution. Ordinarily, once script execution is complete, its objects are destroyed. In many cases, this behavior is just what your application needs. In other cases, however, you will want object persistence across scripts, or for the lifetime of your application. In these cases you need to embed object creation code directly in your application, or you need to tie the object directly to the global object so that it persists as long as the global object itself persists.
Embedding a custom JS object in an application is useful when object persistence is required or when you know that you want an object to be available to several scripts. For example, a custom object that represents a user's ID and access rights may be needed during the entire lifetime of the application. It saves overhead and time to create and populate this object once, instead of recreating it over and over again with a script each time the user's ID or permissions need to be checked.
One way to embed a custom object in an application is to:
JSPropertySpec
data type, and populate it with the property information for your object, including the name of the property's get and set methods.JSFunctionSpec
data type, and populate it with information about the methods used by your object.JS_NewObject
or JS_ConstructObject
to instantiate the object.JS_DefineFunctions
to create the object's methods.JS_DefineProperties
to create the object's properties.The code that describes persistent, custom JS objects should be placed near the start of application execution, before any code that relies upon the prior existence of the object. Embedded engine calls that instantiate and populate the custom object should also appear before any code that relies on the prior existence of the object.
NOTE: An alternate, and in many cases, easier way to create a custom object in application code is to call JS_DefineObject
to create the object, and then make repeated calls to JS_SetProperty
to set the object's properties. For more information about defining an object, see JS_DefineObject
. For more information about setting an object's properties, see JS_SetProperty
.
Like contexts, you can associate large quantities of data with an object without having to store the data in the object itself. Call JS_SetPrivate
to establish a pointer to private data for the object, and call JS_GetPrivate
to retrieve the pointer so that you can access the data. Your application is responsible for creating and managing this optional private data.
To create private data and associate it with an object:
JS_SetPrivate
, specify the object for which to establish private data, and specify the pointer to the data.For example:
JS_SetPrivate(cx, obj, pdata);
To retrieve the data at a later time, call JS_GetPrivate
, and pass the object as an argument. This function returns the pointer to an object's private data:
pdata = JS_GetPrivate(cx, obj);
The JS engine now provides Unicode-enabled versions of many API functions that handle scripts, including JS functions. These functions permit you to pass Unicode-encoded scripts directly to the engine for compilation and execution. The following table lists standard engine functions and their Unicode equivalents:
Unicode-enabled functions work exactly like their traditional namesakes, except that where traditional functions take a char *
argument, the Unicode versions take a jschar *
argument.
JavaScript defines its own data types. Some of these data types correspond directly to their C counterparts. Others, such as JSObject
, jsdouble
, and JSString
, are specific to JavaScript.
Generally, you declare and use JS data types in your application just as you do standard C data types. The JS engine, however, keeps separate track of JS data type variables that require more than a word of storage: JSObject
, jsdouble
, and JSString
. Periodically, the engine examines these variables to see if they are still in use, and if they are not, it garbage collects them, freeing the storage space for reuse.
Garbage collection makes effective reuse of the heap, but overly frequent garbage collection may be a performance issue. You can control the approximate frequency of garbage collection based on the size of the JS run time you allocate for your application in relation to the number of JS variables and objects your application uses. If your application creates and uses many JS objects and variables, you may want to allocate a sufficiently large run time to reduce the likelihood of frequent garbage collection.
NOTE: Your application can also call JS_GC
or JS_MaybeGC
to force garbage collection at any time. JS_GC
forces garbage collection. JS_MaybeGC
performs conditional garbage collection only if a certain percentage of space initially allocated to the run time is in use at the time you invoke the function.
In addition to JS data types, the JS engine also uses JS values, called jsval
s. A jsval
is essentially a pointer to any JS data type except integers. For integers, a jsval
contains the integer value itself. In other cases, the pointer is encoded to contain additional information about the type of data to which it points. Using jsval
s improves engine efficiency, and permits many API functions to handle a variety of underlying data types.
The engine API contains a group of macros that test the JS data type of a jsval
. These are:
Besides testing a jsval
for its underlying data type, you can test it to determine if it is a primitive JS data type (JSVAL_IS_PRIMITIVE
). Primitives are values that are undefined, null, boolean, numeric, or string types.
You can also test the value pointed to by a jsval
to see if it is NULL
(JSVAL_IS_NULL
) or void (JSVAL_IS_VOID
).
If a jsval points to a JS data type of JSObject
, jsdouble
, or jsstr
, you can cast the jsval
to its underlying data type using JSVAL_TO_OBJECT
, JSVAL_TO_DOUBLE
, and JSVAL_TO_STRING
, respectively. This is useful in some cases where your application or a JS engine call requires a variable or argument of a specific data type, rather than a jsval
. Similarly, you can convert a JSObject
, jsdouble
, and jsstr
to a jsval
using OBJECT_TO_JSVAL
, DOUBLE_TO_JSVAL
, and STRING_TO_JSVAL
, respectively.
As with other API calls, the names of Unicode-enabled API string functions correspond one-for-one with the standard engine API string function names as follows: if a standard function name is JS_NewStringCopyN
, the corresponding Unicode version of the function is JS_NewUCStringCopyN
. Unicode-enabled API string functions are also available for interned string.
To save storage space, the JS engine provides support for sharing a single instance of a string among separate invocations. Such shared strings are called "interned strings". Use interned strings when you know that a particular, string of text will be created and used more than once in an application.
The engine API offers several calls for working with interned strings:
JS_InternString
, for creating or reusing a JSString
.JS_InternUCString
, for creating or reusing a Unicode JSString
.JS_InternUCStringN
, for creating or reusing Unicode JSString
of fixed length.With JavaScript 1.3, the JS engine added security-enhanced API functions for compiling and evaluating scripts and functions passed to the engine. The JS security model is based on the Java principals security model. This model provides a common security interface, but the actual security implementation is up to you.
One common way that security is used in a JavaScript-enabled application is to compare script origins and perhaps limit script interactions. For example, you might compare the codebase of two or more scripts in an application and only allow scripts from the same codebase to modify properties of scripts that share codebases.
To implement secure JS, follow these steps:
JSPrincipals
in your application code.JSPrincipals
struct with your security information. This information can include common codebase information.JSPrincipals
struct. The following table lists these API functions and their purposes:Function | Purpose |
---|---|
JS_CompileScriptForPrincipals |
Compiles, but does not execute, a security-enabled script. |
JS_CompileUCScriptForPrincipals |
Compiles, but does not execute, a security-enabled, Unicode-encoded script. |
JS_CompileFunctionForPrincipals |
Creates a security-enabled JS function from a text string. |
JS_CompileUCFunctionForPrincipals |
Creates a JS function with security information from a Unicode-encoded character string. |
JS_EvaluateScriptForPrincipals |
Compiles and executes a security-enabled script. |
JS_EvaluateUCScriptForPrincipals |
Compiles and executes a security-enabled, Unicode-encoded character script. |