Skip to content

Commit 54e8432

Browse files
committed
Take scope out of scope
1 parent d7a3e2f commit 54e8432

File tree

5 files changed

+86
-70
lines changed

5 files changed

+86
-70
lines changed

src/browser/page.zig

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ pub const Page = struct {
8585
// execute any JavaScript
8686
scope: *Env.Scope,
8787

88+
// For a Page we only create one HandleScope and keep it alive for the duration of the page.
89+
// If needed JS Locals' lifetimes can be reduced by layering on additional Scopes.
90+
// Note: An isolate has only 1 scope stack shared by all main and isolated worlds.
91+
handle_scope: Env.HandleScope,
92+
8893
// List of modules currently fetched/loaded.
8994
module_map: std.StringHashMapUnmanaged([]const u8),
9095

@@ -108,9 +113,18 @@ pub const Page = struct {
108113
.window_clicked_event_node = .{ .func = windowClicked },
109114
.request_factory = browser.http_client.requestFactory(browser.notification),
110115
.scope = undefined,
116+
.handle_scope = undefined,
111117
.module_map = .empty,
112118
};
113-
self.scope = try session.executor.startScope(&self.window, self, self, true);
119+
120+
self.scope = try session.executor.startScope(&self.window, self, self);
121+
122+
// Start a scope (stackframe) for JS Local variables.
123+
Env.HandleScope.init(&self.handle_scope, browser.env.isolate);
124+
errdefer self.handle_scope.deinit();
125+
126+
self.scope.enter();
127+
errdefer self.scope.exit();
114128

115129
// load polyfills
116130
try polyfill.load(self.arena, self.scope);

src/browser/session.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ pub const Session = struct {
109109
std.debug.assert(self.page != null);
110110
// Reset all existing callbacks.
111111
self.browser.app.loop.reset();
112+
113+
self.executor.scope.?.exit();
112114
self.executor.endScope();
115+
116+
self.page.?.handle_scope.deinit();
113117
self.page = null;
114118

115119
// clear netsurf memory arena.

src/cdp/cdp.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ const IsolatedWorld = struct {
555555
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
556556
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
557557
if (self.executor.scope != null) return error.Only1IsolatedContextSupported;
558-
_ = try self.executor.startScope(&page.window, page, {}, false);
558+
_ = try self.executor.startScope(&page.window, page, {});
559559
}
560560
};
561561

src/runtime/js.zig

Lines changed: 59 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
321321
// when the handle_scope is freed.
322322
// We also maintain our own "scope_arena" which allows us to have
323323
// all page related memory easily managed.
324-
pub fn startScope(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype, enter: bool) !*Scope {
324+
pub fn startScope(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype) !*Scope {
325325
std.debug.assert(self.scope == null);
326326

327327
const ModuleLoader = switch (@typeInfo(@TypeOf(module_loader))) {
@@ -338,76 +338,63 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
338338
const isolate = env.isolate;
339339
const Global = @TypeOf(global.*);
340340

341-
var context: v8.Context = blk: {
342-
var temp_scope: v8.HandleScope = undefined;
343-
v8.HandleScope.init(&temp_scope, isolate);
344-
defer temp_scope.deinit();
341+
// All locals created in this function are temporary
342+
var temp_scope: v8.HandleScope = undefined;
343+
v8.HandleScope.init(&temp_scope, isolate);
344+
defer temp_scope.deinit();
345345

346-
const js_global = v8.FunctionTemplate.initDefault(isolate);
347-
attachClass(Global, isolate, js_global);
346+
const js_global = v8.FunctionTemplate.initDefault(isolate);
347+
attachClass(Global, isolate, js_global);
348348

349-
const global_template = js_global.getInstanceTemplate();
350-
global_template.setInternalFieldCount(1);
349+
const global_template = js_global.getInstanceTemplate();
350+
global_template.setInternalFieldCount(1);
351351

352-
// All the FunctionTemplates that we created and setup in Env.init
353-
// are now going to get associated with our global instance.
354-
const templates = &self.env.templates;
355-
inline for (Types, 0..) |s, i| {
356-
const Struct = s.defaultValue().?;
357-
const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct));
358-
global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None);
359-
}
352+
// All the FunctionTemplates that we created and setup in Env.init
353+
// are now going to get associated with our global instance.
354+
const templates = &self.env.templates;
355+
inline for (Types, 0..) |s, i| {
356+
const Struct = s.defaultValue().?;
357+
const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct));
358+
global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None);
359+
}
360360

361-
// The global object (Window) has already been hooked into the v8
362-
// engine when the Env was initialized - like every other type.
363-
// But the V8 global is its own FunctionTemplate instance so even
364-
// though it's also a Window, we need to set the prototype for this
365-
// specific instance of the the Window.
366-
if (@hasDecl(Global, "prototype")) {
367-
const proto_type = Receiver(@typeInfo(Global.prototype).pointer.child);
368-
const proto_name = @typeName(proto_type);
369-
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
370-
js_global.inherit(templates[proto_index]);
371-
}
361+
// The global object (Window) has already been hooked into the v8
362+
// engine when the Env was initialized - like every other type.
363+
// But the V8 global is its own FunctionTemplate instance so even
364+
// though it's also a Window, we need to set the prototype for this
365+
// specific instance of the the Window.
366+
if (@hasDecl(Global, "prototype")) {
367+
const proto_type = Receiver(@typeInfo(Global.prototype).pointer.child);
368+
const proto_name = @typeName(proto_type);
369+
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
370+
js_global.inherit(templates[proto_index]);
371+
}
372372

373-
const context_local = v8.Context.init(isolate, global_template, null);
374-
const context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext();
375-
context.enter();
376-
errdefer if (enter) context.exit();
377-
defer if (!enter) context.exit();
378-
379-
// This shouldn't be necessary, but it is:
380-
// https://groups.google.com/g/v8-users/c/qAQQBmbi--8
381-
// TODO: see if newer V8 engines have a way around this.
382-
inline for (Types, 0..) |s, i| {
383-
const Struct = s.defaultValue().?;
384-
385-
if (@hasDecl(Struct, "prototype")) {
386-
const proto_type = Receiver(@typeInfo(Struct.prototype).pointer.child);
387-
const proto_name = @typeName(proto_type);
388-
if (@hasField(TypeLookup, proto_name) == false) {
389-
@compileError("Type '" ++ @typeName(Struct) ++ "' defines an unknown prototype: " ++ proto_name);
390-
}
373+
const context_local = v8.Context.init(isolate, global_template, null);
374+
const context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext();
375+
context.enter();
376+
defer context.exit();
391377

392-
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
393-
const proto_obj = templates[proto_index].getFunction(context).toObject();
378+
// This shouldn't be necessary, but it is:
379+
// https://groups.google.com/g/v8-users/c/qAQQBmbi--8
380+
// TODO: see if newer V8 engines have a way around this.
381+
inline for (Types, 0..) |s, i| {
382+
const Struct = s.defaultValue().?;
394383

395-
const self_obj = templates[i].getFunction(context).toObject();
396-
_ = self_obj.setPrototype(context, proto_obj);
384+
if (@hasDecl(Struct, "prototype")) {
385+
const proto_type = Receiver(@typeInfo(Struct.prototype).pointer.child);
386+
const proto_name = @typeName(proto_type);
387+
if (@hasField(TypeLookup, proto_name) == false) {
388+
@compileError("Type '" ++ @typeName(Struct) ++ "' defines an unknown prototype: " ++ proto_name);
397389
}
398-
}
399-
break :blk context;
400-
};
401390

402-
// For a Page we only create one HandleScope, it is stored in the main World (enter==true). A page can have multple contexts, 1 for each World.
403-
// The main Context/Scope that enters and holds the HandleScope should therefore always be created first. Following other worlds for this page
404-
// like isolated Worlds, will thereby place their objects on the main page's HandleScope. Note: In the furure the number of context will multiply multiple frames support
405-
var handle_scope: ?v8.HandleScope = null;
406-
if (enter) {
407-
handle_scope = @as(v8.HandleScope, undefined);
408-
v8.HandleScope.init(&handle_scope.?, isolate);
391+
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
392+
const proto_obj = templates[proto_index].getFunction(context).toObject();
393+
394+
const self_obj = templates[i].getFunction(context).toObject();
395+
_ = self_obj.setPrototype(context, proto_obj);
396+
}
409397
}
410-
errdefer if (enter) handle_scope.?.deinit();
411398

412399
{
413400
// If we want to overwrite the built-in console, we have to
@@ -424,7 +411,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
424411
.isolate = isolate,
425412
.context = context,
426413
.templates = &env.templates,
427-
.handle_scope = handle_scope,
428414
.call_arena = self.call_arena.allocator(),
429415
.scope_arena = self.scope_arena.allocator(),
430416
.module_loader = .{
@@ -477,13 +463,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
477463
const PersistentObject = v8.Persistent(v8.Object);
478464
const PersistentFunction = v8.Persistent(v8.Function);
479465

466+
pub const HandleScope = v8.HandleScope;
467+
480468
// Loosely maps to a Browser Page.
481469
pub const Scope = struct {
482470
state: State,
483471
isolate: v8.Isolate,
484472
// This context is a persistent object. The persistent needs to be recovered and reset.
485473
context: v8.Context,
486-
handle_scope: ?v8.HandleScope,
487474

488475
// references the Env.template array
489476
templates: []v8.FunctionTemplate,
@@ -568,14 +555,19 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
568555
for (self.callbacks.items) |*cb| {
569556
cb.deinit();
570557
}
571-
if (self.handle_scope) |*scope| {
572-
scope.deinit();
573-
self.context.exit();
574-
}
558+
575559
var presistent_context = v8.Persistent(v8.Context).recoverCast(self.context);
576560
presistent_context.deinit();
577561
}
578562

563+
pub fn enter(self: *Scope) void {
564+
self.context.enter();
565+
}
566+
567+
pub fn exit(self: *Scope) void {
568+
self.context.exit();
569+
}
570+
579571
fn trackCallback(self: *Scope, pf: PersistentFunction) !void {
580572
return self.callbacks.append(self.scope_arena, pf);
581573
}

src/runtime/testing.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
3030
return struct {
3131
env: *Env,
3232
scope: *Env.Scope,
33+
handle_scope: Env.HandleScope,
3334
executor: Env.ExecutionWorld,
3435

3536
pub const Env = js.Env(State, struct {
@@ -52,8 +53,13 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
5253
if (Global == void) &default_global else global,
5354
state,
5455
{},
55-
true,
5656
);
57+
self.scope.enter();
58+
59+
// Start a scope (stackframe) for JS Local variables.
60+
Env.HandleScope.init(&self.handle_scope, self.executor.env.isolate);
61+
errdefer self.handle_scope.deinit();
62+
5763
return self;
5864
}
5965

0 commit comments

Comments
 (0)