aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/bindings/bindings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/bindings/bindings.cpp')
-rw-r--r--src/javascript/jsc/bindings/bindings.cpp399
1 files changed, 331 insertions, 68 deletions
diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp
index 9afe9a00b..a7e2849ad 100644
--- a/src/javascript/jsc/bindings/bindings.cpp
+++ b/src/javascript/jsc/bindings/bindings.cpp
@@ -1,10 +1,14 @@
#include "helpers.h"
#include "root.h"
+#include <JavaScriptCore/AggregateError.h>
+#include <JavaScriptCore/BytecodeIndex.h>
+#include <JavaScriptCore/CodeBlock.h>
#include <JavaScriptCore/Completion.h>
#include <JavaScriptCore/ErrorInstance.h>
#include <JavaScriptCore/ExceptionScope.h>
#include <JavaScriptCore/FunctionConstructor.h>
#include <JavaScriptCore/Identifier.h>
+#include <JavaScriptCore/JSArray.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSCallbackObject.h>
#include <JavaScriptCore/JSClassRef.h>
@@ -16,15 +20,17 @@
#include <JavaScriptCore/JSSet.h>
#include <JavaScriptCore/JSString.h>
#include <JavaScriptCore/ParserError.h>
+#include <JavaScriptCore/ScriptExecutable.h>
#include <JavaScriptCore/StackFrame.h>
+#include <JavaScriptCore/StackVisitor.h>
#include <JavaScriptCore/VM.h>
#include <JavaScriptCore/WasmFaultSignalHandler.h>
#include <wtf/text/ExternalStringImpl.h>
#include <wtf/text/StringCommon.h>
#include <wtf/text/StringImpl.h>
#include <wtf/text/StringView.h>
-#include <wtf/text/WTFString.h>
+#include <wtf/text/WTFString.h>
extern "C" {
// #pragma mark - JSC::PropertyNameArray
@@ -131,18 +137,49 @@ static JSC::JSValue doLink(JSC__JSGlobalObject *globalObject, JSC::JSValue modul
return JSC::linkAndEvaluateModule(globalObject, moduleKey, JSC::JSValue());
}
+JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject *globalObject,
+ JSC__JSValue *errors, uint16_t errors_count,
+ ZigString arg3) {
+ JSC::VM &vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ JSC::JSValue message = JSC::JSValue(JSC::jsOwnedString(vm, Zig::toString(arg3)));
+ JSC::JSValue options = JSC::jsUndefined();
+ JSC::JSArray *array = nullptr;
+ {
+ JSC::ObjectInitializationScope initializationScope(vm);
+ if ((array = JSC::JSArray::tryCreateUninitializedRestricted(
+ initializationScope, nullptr,
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous),
+ errors_count))) {
+
+ for (uint16_t i = 0; i < errors_count; ++i) {
+ array->initializeIndexWithoutBarrier(initializationScope, i,
+ JSC::JSValue::decode(errors[i]));
+ }
+ }
+ }
+ if (!array) {
+ JSC::throwOutOfMemoryError(globalObject, scope);
+ return JSC::JSValue::encode(JSC::JSValue());
+ }
+
+ JSC::Structure *errorStructure = globalObject->errorStructure(JSC::ErrorType::AggregateError);
+ return RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::createAggregateError(
+ globalObject, vm, errorStructure, array, message, options,
+ nullptr, JSC::TypeNothing, false)));
+}
// static JSC::JSNativeStdFunction* resolverFunction;
// static JSC::JSNativeStdFunction* rejecterFunction;
// static bool resolverFunctionInitialized = false;
-static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject *globalObject,
- JSC::CallFrame *callFrame) {
- return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0)));
+JSC__JSValue ZigString__toValue(ZigString arg0, JSC__JSGlobalObject *arg1) {
+ return JSC::JSValue::encode(JSC::JSValue(JSC::jsOwnedString(arg1->vm(), Zig::toString(arg0))));
}
-static JSC::EncodedJSValue rejecterFunctionCallback(JSC::JSGlobalObject *globalObject,
+static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject *globalObject,
JSC::CallFrame *callFrame) {
- return JSC::JSValue::encode(callFrame->argument(0));
+ return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0)));
}
JSC__JSInternalPromise *
@@ -157,9 +194,14 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject *globalObject, Zi
JSC::JSNativeStdFunction *resolverFunction = JSC::JSNativeStdFunction::create(
globalObject->vm(), globalObject, 1, String(), resolverFunctionCallback);
JSC::JSNativeStdFunction *rejecterFunction = JSC::JSNativeStdFunction::create(
- globalObject->vm(), globalObject, 1, String(), rejecterFunctionCallback);
+ globalObject->vm(), globalObject, 1, String(),
+ [&arg1](JSC::JSGlobalObject *globalObject, JSC::CallFrame *callFrame) -> JSC::EncodedJSValue {
+ return JSC::JSValue::encode(
+ JSC::JSInternalPromise::rejectedPromise(globalObject, callFrame->argument(0)));
+ });
+
globalObject->vm().drainMicrotasks();
- auto result = promise->then(globalObject, resolverFunction, nullptr);
+ auto result = promise->then(globalObject, resolverFunction, rejecterFunction);
globalObject->vm().drainMicrotasks();
// if (promise->status(globalObject->vm()) ==
@@ -687,100 +729,321 @@ bWTF__String JSC__JSValue__toWTFString(JSC__JSValue JSValue0, JSC__JSGlobalObjec
return Wrap<WTF::String, bWTF__String>::wrap(value.toWTFString(arg1));
};
-static ZigException fromErrorInstance(JSC::JSGlobalObject *global, JSC::ErrorInstance *err,
- JSC::JSValue val) {
- ZigException except = Zig::ZigExceptionNone;
- JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), val);
- if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) {
- JSC::StackFrame *stack = &err->stackTrace()->first();
- except.sourceURL = Zig::toZigString(stack->sourceURL());
-
- if (stack->hasLineAndColumnInfo()) {
- unsigned lineNumber;
- unsigned column;
- stack->computeLineAndColumn(lineNumber, column);
- except.line = lineNumber;
- except.column = column;
+static void populateStackFrameMetadata(const JSC::StackFrame *stackFrame, ZigStackFrame *frame) {
+ frame->source_url = Zig::toZigString(stackFrame->sourceURL());
+
+ if (stackFrame->isWasmFrame()) {
+ frame->code_type = ZigStackFrameCodeWasm;
+ return;
+ }
+
+ auto m_codeBlock = stackFrame->codeBlock();
+ if (m_codeBlock) {
+ switch (m_codeBlock->codeType()) {
+ case JSC::EvalCode: {
+ frame->code_type = ZigStackFrameCodeEval;
+ return;
+ }
+ case JSC::ModuleCode: {
+ frame->code_type = ZigStackFrameCodeModule;
+ return;
+ }
+ case JSC::GlobalCode: {
+ frame->code_type = ZigStackFrameCodeGlobal;
+ return;
+ }
+ case JSC::FunctionCode: {
+ frame->code_type =
+ !m_codeBlock->isConstructor() ? ZigStackFrameCodeFunction : ZigStackFrameCodeConstructor;
+ break;
+ }
+ default: ASSERT_NOT_REACHED();
}
+ }
+
+ auto calleeCell = stackFrame->callee();
+ if (!calleeCell || !calleeCell->isObject()) return;
+
+ JSC::JSObject *callee = JSC::jsCast<JSC::JSObject *>(calleeCell);
+ // Does the code block have a user-defined name property?
+ JSC::JSValue name = callee->getDirect(m_codeBlock->vm(), m_codeBlock->vm().propertyNames->name);
+ if (name && name.isString()) {
+ auto str = name.toWTFString(m_codeBlock->globalObject());
+ frame->function_name = Zig::toZigString(str);
+ return;
+ }
+
+ /* For functions (either JSFunction or InternalFunction), fallback to their "native" name
+ * property. Based on JSC::getCalculatedDisplayName, "inlining" the
+ * JSFunction::calculatedDisplayName\InternalFunction::calculatedDisplayName calls */
+ if (JSC::JSFunction *function =
+ JSC::jsDynamicCast<JSC::JSFunction *>(m_codeBlock->vm(), callee)) {
+
+ WTF::String actualName = function->name(m_codeBlock->vm());
+ if (!actualName.isEmpty() || function->isHostOrBuiltinFunction()) {
+ frame->function_name = Zig::toZigString(actualName);
+ return;
+ }
+
+ auto inferred_name = function->jsExecutable()->name();
+ frame->function_name = Zig::toZigString(inferred_name.string());
+ }
+
+ if (JSC::InternalFunction *function =
+ JSC::jsDynamicCast<JSC::InternalFunction *>(m_codeBlock->vm(), callee)) {
+ // Based on JSC::InternalFunction::calculatedDisplayName, skipping the "displayName" property
+ frame->function_name = Zig::toZigString(function->name());
+ }
+}
+// Based on
+// https://github.com/mceSystems/node-jsc/blob/master/deps/jscshim/src/shim/JSCStackTrace.cpp#L298
+static void populateStackFramePosition(const JSC::StackFrame *stackFrame, ZigString *source_lines,
+ int32_t *source_line_numbers, uint8_t source_lines_count,
+ ZigStackFramePosition *position) {
+ auto m_codeBlock = stackFrame->codeBlock();
+ if (!m_codeBlock) return;
+
+ JSC::BytecodeIndex bytecodeOffset =
+ stackFrame->hasBytecodeIndex() ? stackFrame->bytecodeIndex() : JSC::BytecodeIndex();
+
+ /* Get the "raw" position info.
+ * Note that we're using m_codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset
+ * rather than m_codeBlock->expressionRangeForBytecodeOffset in order get the "raw" offsets and
+ * avoid the CodeBlock's expressionRangeForBytecodeOffset modifications to the line and column
+ * numbers, (we don't need the column number from it, and we'll calculate the line "fixes"
+ * ourselves). */
+ int startOffset = 0;
+ int endOffset = 0;
+ int divotPoint = 0;
+ unsigned line = 0;
+ unsigned unusedColumn = 0;
+ m_codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeIndex(
+ bytecodeOffset, divotPoint, startOffset, endOffset, line, unusedColumn);
+ divotPoint += m_codeBlock->sourceOffset();
+
+ // TODO: evaluate if using the API from UnlinkedCodeBlock can be used instead of iterating
+ // through source text.
+
+ /* On the first line of the source code, it seems that we need to "fix" the column with the
+ * starting offset. We currently use codeBlock->source()->startPosition().m_column.oneBasedInt()
+ * as the offset in the first line rather than codeBlock->firstLineColumnOffset(), which seems
+ * simpler (and what CodeBlock::expressionRangeForBytecodeOffset does). This is because
+ * firstLineColumnOffset values seems different from what we expect (according to v8's tests)
+ * and I haven't dove into the relevant parts in JSC (yet) to figure out why. */
+ unsigned columnOffset = line ? 0 : m_codeBlock->source().startColumn().zeroBasedInt();
+
+ // "Fix" the line number
+ JSC::ScriptExecutable *executable = m_codeBlock->ownerExecutable();
+ if (std::optional<int> overrideLine = executable->overrideLineNumber(m_codeBlock->vm())) {
+ line = overrideLine.value();
} else {
- // JSC::ErrorInstance marks these as protected.
- // To work around that, we cast as a JSC::JSObject
- // This code path triggers when there was an exception before the code was executed
- // For example, ParserError becomes one of these
- auto source_url_value = obj->getDirect(global->vm(), global->vm().propertyNames->sourceURL);
- auto str = source_url_value.toWTFString(global);
- except.sourceURL = Zig::toZigString(str);
- except.line = obj->getDirect(global->vm(), global->vm().propertyNames->line).toInt32(global);
- except.column =
- obj->getDirect(global->vm(), global->vm().propertyNames->column).toInt32(global);
+ line += executable->firstLine();
+ }
+
+ // Calculate the staring\ending offsets of the entire expression
+ int expressionStart = divotPoint - startOffset;
+ int expressionStop = divotPoint + endOffset;
+
+ // Make sure the range is valid
+ WTF::StringView sourceString = m_codeBlock->source().provider()->source();
+ if (!expressionStop || expressionStart > static_cast<int>(sourceString.length())) { return; }
+
+ // Search for the beginning of the line
+ unsigned int lineStart = expressionStart;
+ while ((lineStart > 0) && ('\n' != sourceString[lineStart - 1])) { lineStart--; }
+ // Search for the end of the line
+ unsigned int lineStop = expressionStop;
+ unsigned int sourceLength = sourceString.length();
+ while ((lineStop < sourceLength) && ('\n' != sourceString[lineStop])) { lineStop++; }
+ if (source_lines_count > 1 && source_lines != nullptr) {
+ auto chars = sourceString.characters8();
+
+ // Most of the time, when you look at a stack trace, you want a couple lines above
+
+ source_lines[0] = {&chars[lineStart], lineStop - lineStart};
+ source_line_numbers[0] = line;
+
+ if (lineStart > 0) {
+ auto byte_offset_in_source_string = lineStart - 1;
+ uint8_t source_line_i = 1;
+ auto remaining_lines_to_grab = source_lines_count - 1;
+
+ while (byte_offset_in_source_string > 0 && remaining_lines_to_grab > 0) {
+ unsigned int end_of_line_offset = byte_offset_in_source_string;
+
+ // This should probably be code points instead of newlines
+ while (byte_offset_in_source_string > 0 && chars[byte_offset_in_source_string] != '\n') {
+ byte_offset_in_source_string--;
+ }
+
+ // We are at the beginning of the line
+ source_lines[source_line_i] = {&chars[byte_offset_in_source_string],
+ end_of_line_offset - byte_offset_in_source_string + 1};
+
+ source_line_numbers[source_line_i] = line - source_line_i;
+ source_line_i++;
+
+ remaining_lines_to_grab--;
+
+ byte_offset_in_source_string -= byte_offset_in_source_string > 0;
+ }
+ }
}
- if (obj->hasProperty(global, global->vm().propertyNames->stack)) {
- except.stack = Zig::toZigString(
- obj->getDirect(global->vm(), global->vm().propertyNames->stack).toWTFString(global));
+ /* Finally, store the source "positions" info.
+ * Notes:
+ * - The retrieved column seem to point the "end column". To make sure we're current, we'll
+ *calculate the columns ourselves, since we've already found where the line starts. Note that in
+ *v8 it should be 0-based here (in contrast the 1-based column number in v8::StackFrame).
+ * - The static_casts are ugly, but comes from differences between JSC and v8's api, and should
+ *be OK since no source should be longer than "max int" chars.
+ * TODO: If expressionStart == expressionStop, then m_endColumn will be equal to m_startColumn.
+ *Should we handle this case?
+ */
+ position->expression_start = expressionStart;
+ position->expression_stop = expressionStop;
+ position->line = WTF::OrdinalNumber::fromOneBasedInt(static_cast<int>(line)).zeroBasedInt();
+ position->column_start = (expressionStart - lineStart) + columnOffset;
+ position->column_stop = position->column_start + (expressionStop - expressionStart);
+ position->line_start = lineStart;
+ position->line_stop = lineStop;
+
+ return;
+}
+static void populateStackFrame(ZigStackTrace *trace, const JSC::StackFrame *stackFrame,
+ ZigStackFrame *frame, bool is_top) {
+ populateStackFrameMetadata(stackFrame, frame);
+ populateStackFramePosition(stackFrame, is_top ? trace->source_lines_ptr : nullptr,
+ is_top ? trace->source_lines_numbers : nullptr,
+ is_top ? trace->source_lines_to_collect : 0, &frame->position);
+}
+static void populateStackTrace(const WTF::Vector<JSC::StackFrame> &frames, ZigStackTrace *trace) {
+ uint8_t frame_i = 0;
+ size_t stack_frame_i = 0;
+ const size_t total_frame_count = frames.size();
+ const uint8_t frame_count =
+ total_frame_count < trace->frames_len ? total_frame_count : trace->frames_len;
+
+ while (frame_i < frame_count && stack_frame_i < total_frame_count) {
+ // Skip native frames
+ while (stack_frame_i < total_frame_count && !(&frames.at(stack_frame_i))->codeBlock() &&
+ !(&frames.at(stack_frame_i))->isWasmFrame()) {
+ stack_frame_i++;
+ }
+ if (stack_frame_i >= total_frame_count) break;
+
+ ZigStackFrame *frame = &trace->frames_ptr[frame_i];
+ populateStackFrame(trace, &frames[stack_frame_i], frame, frame_i == 0);
+ stack_frame_i++;
+ frame_i++;
+ }
+ trace->frames_len = frame_i;
+}
+static void fromErrorInstance(ZigException *except, JSC::JSGlobalObject *global,
+ JSC::ErrorInstance *err, const Vector<JSC::StackFrame> *stackTrace,
+ JSC::JSValue val) {
+ JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), val);
+ if (stackTrace != nullptr && stackTrace->size() > 0) {
+ populateStackTrace(*stackTrace, &except->stack);
+ } else if (err->stackTrace() != nullptr && err->stackTrace()->size() > 0) {
+ populateStackTrace(*err->stackTrace(), &except->stack);
}
- except.code = (unsigned char)err->errorType();
- if (err->isStackOverflowError()) { except.code = 253; }
- if (err->isOutOfMemoryError()) { except.code = 8; }
+ except->code = (unsigned char)err->errorType();
+ if (err->isStackOverflowError()) { except->code = 253; }
+ if (err->isOutOfMemoryError()) { except->code = 8; }
if (obj->hasProperty(global, global->vm().propertyNames->message)) {
- except.message = Zig::toZigString(
+ except->message = Zig::toZigString(
obj->getDirect(global->vm(), global->vm().propertyNames->message).toWTFString(global));
} else {
- except.message = Zig::toZigString(err->sanitizedMessageString(global));
+ except->message = Zig::toZigString(err->sanitizedMessageString(global));
}
- except.name = Zig::toZigString(err->sanitizedNameString(global));
- except.runtime_type = err->runtimeTypeForCause();
-
- except.exception = err;
+ except->name = Zig::toZigString(err->sanitizedNameString(global));
+ except->runtime_type = err->runtimeTypeForCause();
+
+ except->exception = err;
+}
+
+void exceptionFromString(ZigException *except, JSC::JSValue value, JSC::JSGlobalObject *global) {
+ // Fallback case for when it's a user-defined ErrorLike-object that doesn't inherit from
+ // ErrorInstance
+ if (JSC::JSObject *obj = JSC::jsDynamicCast<JSC::JSObject *>(global->vm(), value)) {
+ if (obj->hasProperty(global, global->vm().propertyNames->name)) {
+ auto name_str =
+ obj->getDirect(global->vm(), global->vm().propertyNames->name).toWTFString(global);
+ except->name = Zig::toZigString(name_str);
+ if (name_str == "Error"_s) {
+ except->code = JSErrorCodeError;
+ } else if (name_str == "EvalError"_s) {
+ except->code = JSErrorCodeEvalError;
+ } else if (name_str == "RangeError"_s) {
+ except->code = JSErrorCodeRangeError;
+ } else if (name_str == "ReferenceError"_s) {
+ except->code = JSErrorCodeReferenceError;
+ } else if (name_str == "SyntaxError"_s) {
+ except->code = JSErrorCodeSyntaxError;
+ } else if (name_str == "TypeError"_s) {
+ except->code = JSErrorCodeTypeError;
+ } else if (name_str == "URIError"_s) {
+ except->code = JSErrorCodeURIError;
+ } else if (name_str == "AggregateError"_s) {
+ except->code = JSErrorCodeAggregateError;
+ }
+ }
- return except;
-}
+ if (obj->hasProperty(global, global->vm().propertyNames->message)) {
+ except->message = Zig::toZigString(
+ obj->getDirect(global->vm(), global->vm().propertyNames->message).toWTFString(global));
+ }
-static ZigException exceptionFromString(WTF::String &str) {
- ZigException except = Zig::ZigExceptionNone;
- auto ref = OpaqueJSString::tryCreate(str);
- except.message = ZigString{ref->characters8(), ref->length()};
- ref->ref();
+ if (obj->hasProperty(global, global->vm().propertyNames->sourceURL)) {
+ except->stack.frames_ptr[0].source_url = Zig::toZigString(
+ obj->getDirect(global->vm(), global->vm().propertyNames->sourceURL).toWTFString(global));
+ except->stack.frames_len = 1;
+ }
- return except;
-}
+ if (obj->hasProperty(global, global->vm().propertyNames->line)) {
+ except->stack.frames_ptr[0].position.line =
+ obj->getDirect(global->vm(), global->vm().propertyNames->line).toInt32(global);
+ except->stack.frames_len = 1;
+ }
-static ZigException exceptionFromString(JSC::JSValue value, JSC::JSGlobalObject *global) {
+ return;
+ }
auto scope = DECLARE_THROW_SCOPE(global->vm());
auto str = value.toWTFString(global);
if (scope.exception()) {
scope.clearException();
scope.release();
- return Zig::ZigExceptionNone;
+ return;
}
scope.release();
- ZigException except = Zig::ZigExceptionNone;
auto ref = OpaqueJSString::tryCreate(str);
- except.message = ZigString{ref->characters8(), ref->length()};
+ except->message = ZigString{ref->characters8(), ref->length()};
ref->ref();
-
- return except;
}
-ZigException JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject *arg1) {
+void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject *arg1,
+ ZigException *exception) {
JSC::JSValue value = JSC::JSValue::decode(JSValue0);
- if (JSC::ErrorInstance *error = JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), value)) {
- return fromErrorInstance(arg1, error, value);
- }
-
- if (JSC::Exception *exception = JSC::jsDynamicCast<JSC::Exception *>(arg1->vm(), value)) {
+ if (JSC::Exception *jscException = JSC::jsDynamicCast<JSC::Exception *>(arg1->vm(), value)) {
if (JSC::ErrorInstance *error =
- JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), exception->value())) {
- return fromErrorInstance(arg1, error, value);
+ JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), jscException->value())) {
+ fromErrorInstance(exception, arg1, error, &jscException->stack(), value);
+ return;
}
}
- return exceptionFromString(value, arg1);
+ if (JSC::ErrorInstance *error = JSC::jsDynamicCast<JSC::ErrorInstance *>(arg1->vm(), value)) {
+ fromErrorInstance(exception, arg1, error, nullptr, value);
+ return;
+ }
+
+ exceptionFromString(exception, value, arg1);
}
#pragma mark - JSC::PropertyName