Repository navigation
Expand file tree
/
Copy pathArrayAdapter.mm
More file actions
149 lines (137 loc) · 4.79 KB
/
Copy pathArrayAdapter.mm
File metadata and controls
149 lines (137 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "ArrayAdapter.h"
#include "ArgConverter.h"
#include "DataWrapper.h"
#include "Helpers.h"
#include "Interop.h"
#include "IsolateWrapper.h"
using namespace tns;
using namespace v8;
@implementation ArrayAdapter {
IsolateWrapper* wrapper_;
std::shared_ptr<Persistent<Value>> object_;
// The wrapper this adapter attached to the JS object, or nullptr when the
// field was already taken. The adapter owns the claim exclusively --
// retirement paths leave adapter claims attached -- so -dealloc frees it in
// both isolate states; the field compare below guards the isolate-alive
// path against a slot someone else overwrote.
ObjCDataWrapper* dataWrapper_;
}
- (instancetype)initWithJSObject:(Local<Object>)jsObject isolate:(Isolate*)isolate {
if (self) {
self->wrapper_ = new IsolateWrapper(isolate);
self->object_ = std::make_shared<Persistent<Value>>(isolate, jsObject);
self->wrapper_->GetCache()->Instances[self] = self->object_;
// A JS object's internal field holds at most one wrapper, owned by whoever
// attached it first. An adapter that finds the field taken stays detached
// and never writes or clears it; it still reads the object through object_.
if (tns::GetValue(isolate, jsObject) == nullptr) {
self->dataWrapper_ = new ObjCDataWrapper(self);
self->dataWrapper_->MarkAdapterClaim();
tns::SetValue(isolate, jsObject, self->dataWrapper_);
}
}
return self;
}
- (NSUInteger)count {
auto isolate = wrapper_->Isolate();
if (!wrapper_->IsValid()) {
return 0;
}
NSUInteger result = 0;
// Scopes-before-@throw: a branded escape from the JS boundary is @thrown only
// after the inner block's V8 scopes destruct.
NSException* __strong pendingThrow = nil;
{
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Object> object = self->object_->Get(isolate).As<Object>();
if (object->IsArray()) {
result = object.As<v8::Array>()->Length();
} else {
Local<Context> context = wrapper_->GetCache()->GetContext();
Local<v8::Array> propertyNames;
TryCatch tc(isolate);
if (object->GetPropertyNames(context).ToLocal(&propertyNames)) {
result = propertyNames->Length();
} else {
NSException* ex = ArgConverter::HandleBoundaryException(context, tc);
if (ex != nil) {
pendingThrow = ex;
}
}
}
}
if (pendingThrow != nil) {
@throw pendingThrow;
}
return result;
}
- (id)objectAtIndex:(NSUInteger)index {
auto isolate = wrapper_->Isolate();
if (!wrapper_->IsValid()) {
return nil;
}
if (!(index < [self count])) {
// Out of bounds: return the adapter default rather than aborting.
return nil;
}
id result = nil;
NSException* __strong pendingThrow = nil;
{
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Object> object = self->object_->Get(isolate).As<Object>();
Local<Context> context = wrapper_->GetCache()->GetContext();
Local<Value> item;
TryCatch tc(isolate);
if (!object->Get(context, (uint)index).ToLocal(&item)) {
NSException* ex = ArgConverter::HandleBoundaryException(context, tc);
if (ex != nil) {
pendingThrow = ex;
}
} else if (!item->IsNullOrUndefined()) {
result = Interop::ToObject(context, item);
}
}
if (pendingThrow != nil) {
@throw pendingThrow;
}
return result;
}
- (void)dealloc {
if (wrapper_->IsValid()) {
auto isolate = wrapper_->Isolate();
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
wrapper_->GetCache()->Instances.erase(self);
// Detach and free only a wrapper that is still the one we attached: a
// finalizer or __releaseNativeCounterpart can have retired it already, and
// whatever else sits in the field belongs to another owner. Once the
// isolate is gone the field can no longer be read, so the claim is dropped
// rather than freed blind.
if (dataWrapper_ != nullptr) {
Local<Value> value = self->object_->Get(isolate);
if (tns::GetValue(isolate, value) == dataWrapper_) {
tns::DeleteValue(isolate, value);
delete dataWrapper_;
}
dataWrapper_ = nullptr;
}
self->object_->Reset();
} else if (dataWrapper_ != nullptr) {
// The isolate is gone, and with it the JS object and every reader of the
// claim; no other path deletes one (__releaseNativeCounterpart leaves
// adapter claims attached), so the owner frees it here — adapters
// released after a worker isolate's teardown otherwise leak one wrapper
// each.
delete dataWrapper_;
dataWrapper_ = nullptr;
}
delete wrapper_;
self->object_ = nullptr;
[super dealloc];
}
@end