Skip to content

Commit b1b365f

Browse files
committed
feat: auto gen static strings from json.
1 parent 299cf2f commit b1b365f

22 files changed

+550
-353
lines changed

bridge/bindings/qjs/atom_string.cc

+5
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@
77

88
namespace kraken {
99

10+
JSValue StaticAtomicString::ToQuickJS(JSContext* ctx) const {
11+
return JS_AtomToValue(ctx, atom_);
12+
}
13+
14+
1015
} // namespace kraken

bridge/bindings/qjs/atom_string.h

+40-20
Original file line numberDiff line numberDiff line change
@@ -18,59 +18,79 @@ namespace kraken {
1818
// instances can share their string storage if the strings are
1919
// identical. Comparing two AtomicString instances is much faster than comparing
2020
// two String instances because we just check string storage identity.
21-
//
22-
// AtomicString instances are not thread-safe. An AtomicString instance created
23-
// in a thread must be used only in the creator thread.
24-
class AtomString final {
25-
// ScriptAtom should only allocate at stack.
26-
KRAKEN_DISALLOW_NEW();
21+
class AtomicString {
22+
public:
23+
AtomicString() = default;
24+
AtomicString(JSAtom atom) : atom_(atom) {}
25+
26+
// Return the undefined string value from atom key.
27+
virtual JSValue ToQuickJS(JSContext* ctx) const = 0;
28+
29+
bool operator==(const AtomicString& other) const { return other.atom_ == this->atom_; }
30+
31+
protected:
32+
JSAtom atom_{JS_ATOM_NULL};
33+
};
2734

35+
// AtomicString which holding quickjs built-in atoms string.
36+
// These string are stored in JSRuntime instead of JSContext.
37+
// So it can be used by any JSContext and don't needs to be freed.
38+
class PersistentAtomicString : public AtomicString {
2839
public:
29-
static AtomString Empty(JSContext* ctx) { return AtomString(ctx, JS_ATOM_NULL); };
40+
PersistentAtomicString(JSAtom atom): AtomicString(atom) {};
3041

31-
explicit AtomString(JSContext* ctx, const std::string& string) : ctx_(ctx), atom_(JS_NewAtom(ctx, string.c_str())) {}
32-
explicit AtomString(JSContext* ctx, JSAtom atom) : ctx_(ctx), atom_(JS_DupAtom(ctx, atom)){};
33-
explicit AtomString(JSContext* ctx, JSValue value) : ctx_(ctx), atom_(JS_ValueToAtom(ctx, value)){};
42+
JSValue ToQuickJS(JSContext* ctx) const override;
43+
};
44+
45+
// PeriodicAtomicString holding string atom key created by JSContext.
46+
// Could be freed when string refer_count set to 0.
47+
class PeriodicAtomicString : public AtomicString {
48+
// Should only allocate on stack.
49+
KRAKEN_DISALLOW_NEW();
3450

35-
~AtomString() { JS_FreeAtom(ctx_, atom_); }
51+
public:
52+
static PeriodicAtomicString Empty(JSContext* ctx) { return PeriodicAtomicString(ctx); };
3653

37-
JSValue ToQuickJS() const { return JS_AtomToValue(ctx_, atom_); }
54+
explicit PeriodicAtomicString(JSContext* ctx) : ctx_(ctx), AtomicString(JS_ATOM_NULL) {}
55+
explicit PeriodicAtomicString(JSContext* ctx, const std::string& string) : ctx_(ctx), AtomicString(JS_NewAtom(ctx, string.c_str())) {}
56+
explicit PeriodicAtomicString(JSContext* ctx, JSAtom atom) : ctx_(ctx), AtomicString(JS_DupAtom(ctx, atom)) {};
57+
explicit PeriodicAtomicString(JSContext* ctx, JSValue value) : ctx_(ctx), AtomicString(JS_ValueToAtom(ctx, value)){};
58+
~PeriodicAtomicString() { JS_FreeAtom(ctx_, atom_); }
59+
60+
JSValue ToQuickJS(JSContext* ctx) const { return JS_AtomToValue(ctx, atom_); }
3861

3962
// Copy assignment
40-
AtomString(AtomString const& value) {
63+
PeriodicAtomicString(PeriodicAtomicString const& value) {
4164
if (&value != this) {
4265
atom_ = JS_DupAtom(ctx_, value.atom_);
4366
}
4467
ctx_ = value.ctx_;
4568
};
46-
AtomString& operator=(const AtomString& other) {
69+
PeriodicAtomicString& operator=(const PeriodicAtomicString& other) {
4770
if (&other != this) {
4871
atom_ = JS_DupAtom(ctx_, other.atom_);
4972
}
5073
return *this;
5174
};
5275

5376
// Move assignment
54-
AtomString(AtomString&& value) noexcept {
77+
PeriodicAtomicString(PeriodicAtomicString&& value) noexcept {
5578
if (&value != this) {
5679
atom_ = JS_DupAtom(ctx_, value.atom_);
5780
}
5881
ctx_ = value.ctx_;
5982
};
60-
AtomString& operator=(AtomString&& value) noexcept {
83+
PeriodicAtomicString& operator=(PeriodicAtomicString&& value) noexcept {
6184
if (&value != this) {
6285
atom_ = JS_DupAtom(ctx_, value.atom_);
6386
}
6487
ctx_ = value.ctx_;
6588
return *this;
6689
}
6790

68-
bool operator==(const AtomString& other) const { return other.atom_ == this->atom_; }
69-
7091
private:
71-
AtomString() = delete;
92+
PeriodicAtomicString() = delete;
7293
JSContext* ctx_{nullptr};
73-
JSAtom atom_{JS_ATOM_NULL};
7494
};
7595

7696
} // namespace kraken

bridge/bindings/qjs/converter_impl.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,10 @@ template <>
175175
struct Converter<IDLDOMString> : public ConverterBase<IDLDOMString> {
176176
static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
177177
assert(!JS_IsException(value));
178-
return AtomString(ctx, value);
178+
return AtomicString(ctx, value);
179179
}
180180

181-
static JSValue ToValue(JSContext* ctx, const AtomString& value) { return value.ToQuickJS(); }
181+
static JSValue ToValue(JSContext* ctx, const AtomicString& value) { return value.ToQuickJS(); }
182182
static JSValue ToValue(JSContext* ctx, NativeString* str) { return JS_NewUnicodeString(ctx, str->string, str->length); }
183183
static JSValue ToValue(JSContext* ctx, std::unique_ptr<NativeString> str) { return JS_NewUnicodeString(ctx, str->string, str->length); }
184184
static JSValue ToValue(JSContext* ctx, uint16_t* bytes, size_t length) { return JS_NewUnicodeString(ctx, bytes, length); }
@@ -189,7 +189,7 @@ template <>
189189
struct Converter<IDLOptional<IDLDOMString>> : public ConverterBase<IDLDOMString> {
190190
static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
191191
if (JS_IsUndefined(value))
192-
return AtomString::Empty(ctx);
192+
return AtomicString::Empty(ctx);
193193
return Converter<IDLDOMString>::FromValue(ctx, value, exception_state);
194194
}
195195

@@ -202,7 +202,7 @@ template <>
202202
struct Converter<IDLNullable<IDLDOMString>> : public ConverterBase<IDLDOMString> {
203203
static ImplType FromValue(JSContext* ctx, JSValue value, ExceptionState& exception_state) {
204204
if (JS_IsNull(value))
205-
return AtomString::Empty(ctx);
205+
return AtomicString::Empty(ctx);
206206
return Converter<IDLDOMString>::FromValue(ctx, value, exception_state);
207207
}
208208
};

bridge/bindings/qjs/idl_type.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct IDLDouble final : public IDLTypeBaseHelper<double> {};
4747
class NativeString;
4848
// DOMString is UTF-16 strings.
4949
// https://stackoverflow.com/questions/35123890/what-is-a-domstring-really
50-
struct IDLDOMString final : public IDLTypeBaseHelper<AtomString> {};
50+
struct IDLDOMString final : public IDLTypeBaseHelper<AtomicString> {};
5151

5252
// https://developer.mozilla.org/en-US/docs/Web/API/USVString
5353
struct IDLUSVString final : public IDLTypeBaseHelper<std::string> {};

bridge/bindings/qjs/js_based_event_listener.cc

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ void JSBasedEventListener::Invoke(ExecutingContext* context, Event* event) {
2121
InvokeInternal(*event->currentTarget(), *event, exception_state);
2222
}
2323

24+
JSBasedEventListener::JSBasedEventListener() {}
25+
2426
}

bridge/bindings/qjs/js_based_event_listener.h

+1-10
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ class JSBasedEventListener : public EventListener {
2121
// Implements step 2. of "inner invoke".
2222
// See: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
2323
void Invoke(ExecutingContext* context, Event* event) final;
24-
25-
// Implements "get the current value of the event handler".
26-
// https://html.spec.whatwg.org/C/#getting-the-current-value-of-the-event-handler
27-
// Returns null with firing error event instead of throwing an exception.
28-
virtual JSValue GetListenerObject(EventTarget&) = 0;
29-
3024
// Returns Functions that handles invoked event or undefined without
3125
// throwing any exception.
3226
virtual JSValue GetEffectiveFunction(EventTarget&) = 0;
@@ -35,11 +29,8 @@ class JSBasedEventListener : public EventListener {
3529
virtual bool IsJSEventHandler() const { return false; }
3630

3731
protected:
38-
JSBasedEventListener() = default;
32+
JSBasedEventListener();
3933

40-
virtual JSContext* GetJSContext() const = 0;
41-
// Returns the ScriptState of the relevant realm of the callback object.
42-
virtual ScriptState* GetScriptState() const = 0;
4334
private:
4435
// Performs "call a user object's operation", required in "inner-invoke".
4536
// "The event handler processing algorithm" corresponds to this in the case of

bridge/bindings/qjs/js_event_handler.cc

+159
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,162 @@
44
*/
55

66
#include "js_event_handler.h"
7+
#include "core/dom/events/error_event.h"
8+
9+
namespace kraken {
10+
11+
std::unique_ptr<JSEventHandler> JSEventHandler::CreateOrNull(JSContext* ctx, JSValue value, JSEventHandler::HandlerType handler_type) {
12+
if (!JS_IsFunction(ctx, value)) {
13+
return nullptr;
14+
}
15+
16+
return std::make_unique<JSEventHandler>(QJSFunction::Create(ctx, value), handler_type);
17+
}
18+
19+
bool JSEventHandler::Matches(const EventListener& other) const {
20+
return this == &other;
21+
}
22+
23+
// https://html.spec.whatwg.org/C/#the-event-handler-processing-algorithm
24+
void JSEventHandler::InvokeInternal(EventTarget& event_target, Event& event, ExceptionState& exception_state) {
25+
// Step 3. Let special error event handling be true if event is an ErrorEvent
26+
// object, event's type is error, and event's currentTarget implements the
27+
// WindowOrWorkerGlobalScope mixin. Otherwise, let special error event
28+
// handling be false.
29+
const bool special_error_event_handling =
30+
IsA<ErrorEvent>(event) && event.type() == event_type_names::kError &&
31+
event.currentTarget()->IsWindowOrWorkerGlobalScope();
32+
33+
// Step 4. Process the Event object event as follows:
34+
// If special error event handling is true
35+
// Invoke callback with five arguments, the first one having the value of
36+
// event's message attribute, the second having the value of event's
37+
// filename attribute, the third having the value of event's lineno
38+
// attribute, the fourth having the value of event's colno attribute, the
39+
// fifth having the value of event's error attribute, and with the
40+
// callback this value set to event's currentTarget. Let return value be
41+
// the callback's return value.
42+
// Otherwise
43+
// Invoke callback with one argument, the value of which is the Event
44+
// object event, with the callback this value set to event's
45+
// currentTarget. Let return value be the callback's return value.
46+
// If an exception gets thrown by the callback, end these steps and allow
47+
// the exception to propagate. (It will propagate to the DOM event dispatch
48+
// logic, which will then report the exception.)
49+
HeapVector<ScriptValue> arguments;
50+
ScriptState* script_state_of_listener =
51+
event_handler_->CallbackRelevantScriptState();
52+
v8::Isolate* isolate = script_state_of_listener->GetIsolate();
53+
54+
if (special_error_event_handling) {
55+
auto* error_event = To<ErrorEvent>(&event);
56+
57+
// The error argument should be initialized to null for dedicated workers.
58+
// https://html.spec.whatwg.org/C/#runtime-script-errors-2
59+
ScriptValue error_attribute = error_event->error(script_state_of_listener);
60+
if (error_attribute.IsEmpty() ||
61+
error_event->target()->InterfaceName() == event_target_names::kWorker) {
62+
error_attribute = ScriptValue::CreateNull(isolate);
63+
}
64+
arguments = {
65+
ScriptValue(isolate,
66+
ToV8Traits<IDLString>::ToV8(script_state_of_listener,
67+
error_event->message())
68+
.ToLocalChecked()),
69+
ScriptValue(isolate,
70+
ToV8Traits<IDLString>::ToV8(script_state_of_listener,
71+
error_event->filename())
72+
.ToLocalChecked()),
73+
ScriptValue(isolate,
74+
ToV8Traits<IDLUnsignedLong>::ToV8(script_state_of_listener,
75+
error_event->lineno())
76+
.ToLocalChecked()),
77+
ScriptValue(isolate, ToV8Traits<IDLUnsignedLong>::ToV8(
78+
script_state_of_listener, error_event->colno())
79+
.ToLocalChecked()),
80+
error_attribute};
81+
} else {
82+
arguments.push_back(ScriptValue(isolate, js_event));
83+
}
84+
85+
if (!event_handler_->IsRunnableOrThrowException(
86+
event.ShouldDispatchEvenWhenExecutionContextIsPaused()
87+
? V8EventHandlerNonNull::IgnorePause::kIgnore
88+
: V8EventHandlerNonNull::IgnorePause::kDontIgnore)) {
89+
return;
90+
}
91+
ScriptValue result;
92+
if (!event_handler_
93+
->InvokeWithoutRunnabilityCheck(event.currentTarget(), arguments)
94+
.To(&result) ||
95+
isolate->IsExecutionTerminating())
96+
return;
97+
v8::Local<v8::Value> v8_return_value = result.V8Value();
98+
99+
// There is nothing to do if |v8_return_value| is null or undefined.
100+
// See Step 5. for more information.
101+
if (v8_return_value->IsNullOrUndefined())
102+
return;
103+
104+
// https://webidl.spec.whatwg.org/#invoke-a-callback-function
105+
// step 13: Set completion to the result of converting callResult.[[Value]] to
106+
// an IDL value of the same type as the operation's return type.
107+
//
108+
// OnBeforeUnloadEventHandler returns DOMString? while OnErrorEventHandler and
109+
// EventHandler return any, so converting |v8_return_value| to return type is
110+
// necessary only for OnBeforeUnloadEventHandler.
111+
String result_for_beforeunload;
112+
if (IsOnBeforeUnloadEventHandler()) {
113+
event_handler_->EvaluateAsPartOfCallback(Bind(
114+
[](v8::Local<v8::Value>& v8_return_value,
115+
String& result_for_beforeunload) {
116+
// TODO(yukiy): use |NativeValueTraits|.
117+
V8StringResource<kTreatNullAsNullString> native_result(
118+
v8_return_value);
119+
120+
// |native_result.Prepare()| throws exception if it fails to convert
121+
// |native_result| to String.
122+
if (!native_result.Prepare())
123+
return;
124+
result_for_beforeunload = native_result;
125+
},
126+
std::ref(v8_return_value), std::ref(result_for_beforeunload)));
127+
if (!result_for_beforeunload)
128+
return;
129+
}
130+
131+
// Step 5. Process return value as follows:
132+
// If event is a BeforeUnloadEvent object and event's type is beforeunload
133+
// If return value is not null, then:
134+
// 1. Set event's canceled flag.
135+
// 2. If event's returnValue attribute's value is the empty string, then
136+
// set event's returnValue attribute's value to return value.
137+
// If special error event handling is true
138+
// If return value is true, then set event's canceled flag.
139+
// Otherwise
140+
// If return value is false, then set event's canceled flag.
141+
// Note: If we've gotten to this "Otherwise" clause because event's type
142+
// is beforeunload but event is not a BeforeUnloadEvent object,
143+
// then return value will never be false, since in such cases
144+
// return value will have been coerced into either null or a
145+
// DOMString.
146+
auto* before_unload_event = DynamicTo<BeforeUnloadEvent>(&event);
147+
const bool is_beforeunload_event =
148+
before_unload_event && event.type() == event_type_names::kBeforeunload;
149+
if (is_beforeunload_event) {
150+
if (result_for_beforeunload) {
151+
event.preventDefault();
152+
if (before_unload_event->returnValue().IsEmpty())
153+
before_unload_event->setReturnValue(result_for_beforeunload);
154+
}
155+
} else if (!IsOnBeforeUnloadEventHandler()) {
156+
if (special_error_event_handling && v8_return_value->IsBoolean() &&
157+
v8_return_value.As<v8::Boolean>()->Value())
158+
event.preventDefault();
159+
else if (!special_error_event_handling && v8_return_value->IsBoolean() &&
160+
!v8_return_value.As<v8::Boolean>()->Value())
161+
event.preventDefault();
162+
}
163+
}
164+
165+
}

bridge/bindings/qjs/js_event_handler.h

+30-2
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,45 @@ class JSEventHandler : public JSBasedEventListener {
2525
kOnBeforeUnloadEventHandler,
2626
};
2727

28-
static std::unique_ptr<JSEventHandler> CreateOrNull(JSContext* ctx, HandlerType handler_type);
28+
static std::unique_ptr<JSEventHandler> CreateOrNull(JSContext* ctx, JSValue value, HandlerType handler_type);
2929
static JSValue ToQuickJS(JSContext* ctx, EventTarget* event_target, EventListener* listener) {
3030
if (auto* event_handler = DynamicTo<JSEventHandler>(listener)) {
31-
return event_handler->GetListenerObject(*event_target);
31+
return event_handler->GetEffectiveFunction(*event_target);
3232
}
3333
return JS_NULL;
3434
}
3535

36+
explicit JSEventHandler(const std::shared_ptr<QJSFunction>& event_handler, HandlerType type): type_(type), event_handler_(event_handler) {};
37+
38+
JSValue GetEffectiveFunction(EventTarget&) {
39+
return event_handler_->ToQuickJS();
40+
}
41+
42+
// Helper functions for DowncastTraits.
43+
bool IsJSEventHandler() const override { return true; }
44+
45+
// For checking special types of EventHandler.
46+
bool IsOnErrorEventHandler() const {
47+
return type_ == HandlerType::kOnErrorEventHandler;
48+
}
49+
50+
bool IsOnBeforeUnloadEventHandler() const {
51+
return type_ == HandlerType::kOnBeforeUnloadEventHandler;
52+
}
53+
54+
// EventListener overrides:
55+
bool Matches(const EventListener&) const override;
3656

3757
private:
58+
// JSBasedEventListener override:
59+
// Performs "The event handler processing algorithm"
60+
// https://html.spec.whatwg.org/C/#the-event-handler-processing-algorithm
61+
void InvokeInternal(EventTarget&,
62+
Event&,
63+
ExceptionState& exception_state) override;
3864

65+
std::shared_ptr<QJSFunction> event_handler_;
66+
const HandlerType type_;
3967
};
4068

4169
}

0 commit comments

Comments
 (0)