You can write code against the raw JavaScriptCore C API directly. This C API sits under the `ultralight::js` typed layer and ships fully supported with the SDK. Use it to port existing JavaScriptCore code or to access features the typed layer omits. For most tasks the typed layer is still the easier path.

## Getting a Context

To get a raw context, call `LockJSContext()` on your View (passing an iframe's name attribute to access that iframe) and include `<JavaScriptCore/JavaScript.h>`.

```cpp
#include <JavaScriptCore/JavaScript.h>

void MyApp::OnDOMReady(View* caller, uint64_t frame_id,
                       bool is_main_frame, const String& url) {
  RefPtr<JSContext> context = caller->LockJSContext();
  if (!context)
    return;
  JSContextRef ctx = context->ctx();

  JSStringRef script = JSStringCreateWithUTF8CString("startGame()");
  JSEvaluateScript(ctx, script, nullptr, nullptr, 0, nullptr);
  JSStringRelease(script);
}
```

The call returns a `RefPtr` that locks the JavaScript VM until it goes out of scope (returning `nullptr` when the page cannot run scripts). Call `ctx()` on it to get the `JSContextRef`.

> 🚧 Contexts Reset on Navigation
>
> The context resets on every navigation. You must get it again for each page. `LoadListener::OnWindowObjectReady()` or `LoadListener::OnDOMReady()` are good places to set up state.

### Retrieving From Typed Code

To get the underlying `JSContextRef` from an existing `js::Context`, call `ulJSContextGetJSContextRef()` (call this only on the Renderer's thread and never store the result).

```cpp
js::Context ctx(caller);
JSContextRef jsc_ctx = ulJSContextGetJSContextRef(ctx.raw());
```

## What the Fork Adds

The library uses a fork of JavaScriptCore that adds to the classic C API. You can find the details in the headers for these additions: [JSObjectRef.h](/api/cpp/2_0_0/_j_s_object_ref_8h.html), [JSValueRef.h](/api/cpp/2_0_0/_j_s_value_ref_8h.html) and [JSTypedArray.h](/api/cpp/2_0_0/_j_s_typed_array_8h.html).

| Addition | Description |
|---|---|
| `JSObjectMakeFunctionWithData` | Creates a real function object with a user-data pointer and a finalizer. Script methods like bind, call and apply work normally. |
| UTF-8 property functions | Get, set and define properties by UTF-8 name (`JSObjectGetPropertiesUTF8`, `JSObjectSetPropertiesUTF8`, `JSObjectMakeWithPropertiesUTF8`, `JSObjectGetOwnEntriesUTF8`, `JSObjectDefinePropertyUTF8`, `JSObjectDefineAccessorUTF8`). You can assign several properties in one call. `JSObjectFreeze` maps to `Object.freeze`. |
| `JSObjectMakeTypedError`, `JSValueGetErrorDetails` | Create an error of a specific type and read its type, message, source location and stack in one call. |
| `JSPromiseGetStatus`, `JSPromiseGetResult`, `JSPromiseMarkAsHandled` | Read a promise's state and result without scheduling a reaction. You can mark a rejection that you handled natively. |
| `JSArrayBufferDetach`, `JSArrayBufferIsDetached` | Free memory shared with a buffer so that later script access predictably fails. |
| `JSObjectSetExternalMemoryHint` | Tell the garbage collector how much native memory a small wrapper keeps alive. |
| `JSValueIsFunctionObject` | Returns true only for an actual function object (`JSObjectIsFunction` also returns true for anything callable like a callable Proxy). |
| `JSClassDefinition` version 1000 | Callbacks ending in `Ex` receive the `JSClassRef`, and you gain access to per-class private data (`JSClassGetPrivate` and `JSClassSetPrivate`). Version 0 remains unchanged. |

For example, you can create a frozen `display` object and define several properties in one call.

```cpp
const char* names[] = { "width", "height" };
JSValueRef values[] = { JSValueMakeNumber(ctx, 1280),
                        JSValueMakeNumber(ctx, 720) };
JSObjectRef display =
    JSObjectMakeWithPropertiesUTF8(ctx, names, values, 2, nullptr);
JSObjectFreeze(ctx, display, nullptr);

JSObjectRef global = JSContextGetGlobalObject(ctx);
JSObjectDefinePropertyUTF8(ctx, global, "display", display,
                           kJSPropertyAttributeReadOnly, nullptr);
```

## Mixing Raw and Typed Code

There is no conversion step between a `JSValueRef` and a `js::Value`. The values meet on the page.

Raw values behave like ordinary page JavaScript so page script sees them as normal variables.

```js
console.log(display.width);  // 1280
display.width = 1920;        // fails: display is frozen
```

The typed layer reads those values by name like any other page value.

```cpp
js::Context ctx(caller);
double width = ctx["display"]["width"].Or(0.0);
```

> 🚧 Raw Ownership Rules
>
> The raw layer follows classic ownership rules. Anything you create you must release (like `JSStringCreateWithUTF8CString` and `JSStringRelease`, or `JSClassCreate` and `JSClassRelease`). A `JSValueRef` kept past the current call needs `JSValueProtect` and `JSValueUnprotect`. The typed layer's handles manage themselves.

If you are porting `JSHelpers` or 1.4-era JavaScriptCore code, see the tables in [Porting from 1.4 to 2.0](/docs/2.0/migrating-from-1-4-to-2-0).
