diff options
author | 2021-06-24 22:55:42 -0700 | |
---|---|---|
committer | 2021-06-24 22:55:42 -0700 | |
commit | 3a95a74b7feadb59a215ff06446ccebff4a4008e (patch) | |
tree | 81fda0b8e3d27689edf75534238ea1a5ca102377 | |
parent | c3f9d77391589b65951616a632af87107fba469f (diff) | |
download | bun-3a95a74b7feadb59a215ff06446ccebff4a4008e.tar.gz bun-3a95a74b7feadb59a215ff06446ccebff4a4008e.tar.zst bun-3a95a74b7feadb59a215ff06446ccebff4a4008e.zip |
I like this direction
-rw-r--r-- | build.zig | 1 | ||||
-rw-r--r-- | src/javascript/jsc/JavascriptCore.zig | 238 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 534 | ||||
-rw-r--r-- | src/js_lexer.zig | 29 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 1 | ||||
-rw-r--r-- | src/options.zig | 5 | ||||
-rw-r--r-- | src/runtime.footer.js | 10 | ||||
-rw-r--r-- | src/runtime.version | 2 | ||||
-rw-r--r-- | src/string_immutable.zig | 11 | ||||
-rw-r--r-- | src/wtf_string_mutable.zig | 243 |
10 files changed, 1047 insertions, 27 deletions
@@ -27,6 +27,7 @@ pub fn build(b: *std.build.Builder) void { var cwd_buf = [_]u8{0} ** 4096; var cwd = std.os.getcwd(&cwd_buf) catch unreachable; var exe: *std.build.LibExeObjStep = undefined; + var javascript: *std.build.LibExeObjStep = undefined; var output_dir_buf = std.mem.zeroes([4096]u8); var bin_label = if (mode == std.builtin.Mode.Debug) "/debug/" else "/"; diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig new file mode 100644 index 000000000..1d5806fce --- /dev/null +++ b/src/javascript/jsc/JavascriptCore.zig @@ -0,0 +1,238 @@ +pub const struct_OpaqueJSContextGroup = opaque {}; +pub const JSContextGroupRef = ?*const struct_OpaqueJSContextGroup; +pub const struct_OpaqueJSContext = opaque {}; +pub const JSContextRef = ?*const struct_OpaqueJSContext; +pub const JSGlobalContextRef = ?*struct_OpaqueJSContext; +pub const struct_OpaqueJSString = opaque {}; +pub const JSStringRef = ?*struct_OpaqueJSString; +pub const struct_OpaqueJSClass = opaque {}; +pub const JSClassRef = ?*struct_OpaqueJSClass; +pub const struct_OpaqueJSPropertyNameArray = opaque {}; +pub const JSPropertyNameArrayRef = ?*struct_OpaqueJSPropertyNameArray; +pub const struct_OpaqueJSPropertyNameAccumulator = opaque {}; +pub const JSPropertyNameAccumulatorRef = ?*struct_OpaqueJSPropertyNameAccumulator; +pub const JSTypedArrayBytesDeallocator = ?fn (?*c_void, ?*c_void) callconv(.C) void; +pub const struct_OpaqueJSValue = opaque {}; +pub const JSValueRef = ?*const struct_OpaqueJSValue; +pub const JSObjectRef = ?*struct_OpaqueJSValue; +pub extern fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) JSValueRef; +pub extern fn JSCheckScriptSyntax(ctx: JSContextRef, script: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) bool; +pub extern fn JSGarbageCollect(ctx: JSContextRef) void; +pub const JSType = enum(c_uint) { + kJSTypeUndefined, + kJSTypeNull, + kJSTypeBoolean, + kJSTypeNumber, + kJSTypeString, + kJSTypeObject, + kJSTypeSymbol, + _, +}; +pub const kJSTypeUndefined = @enumToInt(JSType.kJSTypeUndefined); +pub const kJSTypeNull = @enumToInt(JSType.kJSTypeNull); +pub const kJSTypeBoolean = @enumToInt(JSType.kJSTypeBoolean); +pub const kJSTypeNumber = @enumToInt(JSType.kJSTypeNumber); +pub const kJSTypeString = @enumToInt(JSType.kJSTypeString); +pub const kJSTypeObject = @enumToInt(JSType.kJSTypeObject); +pub const kJSTypeSymbol = @enumToInt(JSType.kJSTypeSymbol); +pub const JSTypedArrayType = enum(c_uint) { + kJSTypedArrayTypeInt8Array, + kJSTypedArrayTypeInt16Array, + kJSTypedArrayTypeInt32Array, + kJSTypedArrayTypeUint8Array, + kJSTypedArrayTypeUint8ClampedArray, + kJSTypedArrayTypeUint16Array, + kJSTypedArrayTypeUint32Array, + kJSTypedArrayTypeFloat32Array, + kJSTypedArrayTypeFloat64Array, + kJSTypedArrayTypeArrayBuffer, + kJSTypedArrayTypeNone, + _, +}; +pub const kJSTypedArrayTypeInt8Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeInt8Array); +pub const kJSTypedArrayTypeInt16Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeInt16Array); +pub const kJSTypedArrayTypeInt32Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeInt32Array); +pub const kJSTypedArrayTypeUint8Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeUint8Array); +pub const kJSTypedArrayTypeUint8ClampedArray = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeUint8ClampedArray); +pub const kJSTypedArrayTypeUint16Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeUint16Array); +pub const kJSTypedArrayTypeUint32Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeUint32Array); +pub const kJSTypedArrayTypeFloat32Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeFloat32Array); +pub const kJSTypedArrayTypeFloat64Array = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeFloat64Array); +pub const kJSTypedArrayTypeArrayBuffer = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeArrayBuffer); +pub const kJSTypedArrayTypeNone = @enumToInt(JSTypedArrayType.kJSTypedArrayTypeNone); +pub extern fn JSValueGetType(ctx: JSContextRef, value: JSValueRef) JSType; +pub extern fn JSValueIsUndefined(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsNull(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsBoolean(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsNumber(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsString(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsSymbol(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsObject(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsObjectOfClass(ctx: JSContextRef, value: JSValueRef, jsClass: JSClassRef) bool; +pub extern fn JSValueIsArray(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueIsDate(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueGetTypedArrayType(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSTypedArrayType; +pub extern fn JSValueIsEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern fn JSValueIsStrictEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef) bool; +pub extern fn JSValueIsInstanceOfConstructor(ctx: JSContextRef, value: JSValueRef, constructor: JSObjectRef, exception: [*c]JSValueRef) bool; +pub extern fn JSValueMakeUndefined(ctx: JSContextRef) JSValueRef; +pub extern fn JSValueMakeNull(ctx: JSContextRef) JSValueRef; +pub extern fn JSValueMakeBoolean(ctx: JSContextRef, boolean: bool) JSValueRef; +pub extern fn JSValueMakeNumber(ctx: JSContextRef, number: f64) JSValueRef; +pub extern fn JSValueMakeString(ctx: JSContextRef, string: JSStringRef) JSValueRef; +pub extern fn JSValueMakeSymbol(ctx: JSContextRef, description: JSStringRef) JSValueRef; +pub extern fn JSValueMakeFromJSONString(ctx: JSContextRef, string: JSStringRef) JSValueRef; +pub extern fn JSValueCreateJSONString(ctx: JSContextRef, value: JSValueRef, indent: c_uint, exception: [*c]JSValueRef) JSStringRef; +pub extern fn JSValueToBoolean(ctx: JSContextRef, value: JSValueRef) bool; +pub extern fn JSValueToNumber(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) f64; +pub extern fn JSValueToStringCopy(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSStringRef; +pub extern fn JSValueToObject(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSValueProtect(ctx: JSContextRef, value: JSValueRef) void; +pub extern fn JSValueUnprotect(ctx: JSContextRef, value: JSValueRef) void; +pub const JSPropertyAttributes = enum(c_uint) { + kJSPropertyAttributeNone = 0, + kJSPropertyAttributeReadOnly = 2, + kJSPropertyAttributeDontEnum = 4, + kJSPropertyAttributeDontDelete = 8, + _, +}; +pub const kJSPropertyAttributeNone = @enumToInt(JSPropertyAttributes.kJSPropertyAttributeNone); +pub const kJSPropertyAttributeReadOnly = @enumToInt(JSPropertyAttributes.kJSPropertyAttributeReadOnly); +pub const kJSPropertyAttributeDontEnum = @enumToInt(JSPropertyAttributes.kJSPropertyAttributeDontEnum); +pub const kJSPropertyAttributeDontDelete = @enumToInt(JSPropertyAttributes.kJSPropertyAttributeDontDelete); +pub const JSClassAttributes = enum(c_uint) { + kJSClassAttributeNone = 0, + kJSClassAttributeNoAutomaticPrototype = 2, + _, +}; + +pub const kJSClassAttributeNone = @enumToInt(JSClassAttributes.kJSClassAttributeNone); +pub const kJSClassAttributeNoAutomaticPrototype = @enumToInt(JSClassAttributes.kJSClassAttributeNoAutomaticPrototype); +pub const JSObjectInitializeCallback = ?fn (JSContextRef, JSObjectRef) callconv(.C) void; +pub const JSObjectFinalizeCallback = ?fn (JSObjectRef) callconv(.C) void; +pub const JSObjectHasPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef) callconv(.C) bool; +pub const JSObjectGetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, [*c]JSValueRef) callconv(.C) JSValueRef; +pub const JSObjectSetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, JSValueRef, [*c]JSValueRef) callconv(.C) bool; +pub const JSObjectDeletePropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, [*c]JSValueRef) callconv(.C) bool; +pub const JSObjectGetPropertyNamesCallback = ?fn (JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef) callconv(.C) void; +pub const JSObjectCallAsFunctionCallback = ?fn (ctx: JSContextRef, function: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) callconv(.C) JSValueRef; +pub const JSObjectCallAsConstructorCallback = ?fn (JSContextRef, JSObjectRef, usize, [*c]const JSValueRef, [*c]JSValueRef) callconv(.C) JSObjectRef; +pub const JSObjectHasInstanceCallback = ?fn (JSContextRef, JSObjectRef, JSValueRef, [*c]JSValueRef) callconv(.C) bool; +pub const JSObjectConvertToTypeCallback = ?fn (JSContextRef, JSObjectRef, JSType, [*c]JSValueRef) callconv(.C) JSValueRef; +pub const JSStaticValue = extern struct { + name: [*c]const u8, + getProperty: JSObjectGetPropertyCallback, + setProperty: JSObjectSetPropertyCallback, + attributes: JSPropertyAttributes, +}; +pub const JSStaticFunction = extern struct { + name: [*c]const u8, + callAsFunction: JSObjectCallAsFunctionCallback, + attributes: JSPropertyAttributes, +}; +pub const JSClassDefinition = extern struct { + version: c_int, + attributes: JSClassAttributes, + className: [*c]const u8, + parentClass: JSClassRef, + staticValues: [*c]const JSStaticValue, + staticFunctions: [*c]const JSStaticFunction, + initialize: JSObjectInitializeCallback, + finalize: JSObjectFinalizeCallback, + hasProperty: JSObjectHasPropertyCallback, + getProperty: JSObjectGetPropertyCallback, + setProperty: JSObjectSetPropertyCallback, + deleteProperty: JSObjectDeletePropertyCallback, + getPropertyNames: JSObjectGetPropertyNamesCallback, + callAsFunction: JSObjectCallAsFunctionCallback, + callAsConstructor: JSObjectCallAsConstructorCallback, + hasInstance: JSObjectHasInstanceCallback, + convertToType: JSObjectConvertToTypeCallback, +}; +pub extern const kJSClassDefinitionEmpty: JSClassDefinition; +pub extern fn JSClassCreate(definition: [*c]const JSClassDefinition) JSClassRef; +pub extern fn JSClassRetain(jsClass: JSClassRef) JSClassRef; +pub extern fn JSClassRelease(jsClass: JSClassRef) void; +pub extern fn JSObjectMake(ctx: JSContextRef, jsClass: JSClassRef, data: ?*c_void) JSObjectRef; +pub extern fn JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) JSObjectRef; +pub extern fn JSObjectMakeConstructor(ctx: JSContextRef, jsClass: JSClassRef, callAsConstructor: JSObjectCallAsConstructorCallback) JSObjectRef; +pub extern fn JSObjectMakeArray(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeDate(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeError(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeRegExp(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: [*c]JSObjectRef, reject: [*c]JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeFunction(ctx: JSContextRef, name: JSStringRef, parameterCount: c_uint, parameterNames: [*c]const JSStringRef, body: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectGetPrototype(ctx: JSContextRef, object: JSObjectRef) JSValueRef; +pub extern fn JSObjectSetPrototype(ctx: JSContextRef, object: JSObjectRef, value: JSValueRef) void; +pub extern fn JSObjectHasProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef) bool; +pub extern fn JSObjectGetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) JSValueRef; +pub extern fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: [*c]JSValueRef) void; +pub extern fn JSObjectDeleteProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) bool; +pub extern fn JSObjectHasPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern fn JSObjectGetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) JSValueRef; +pub extern fn JSObjectSetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: [*c]JSValueRef) void; +pub extern fn JSObjectDeletePropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern fn JSObjectGetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, exception: [*c]JSValueRef) JSValueRef; +pub extern fn JSObjectSetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, value: JSValueRef, exception: [*c]JSValueRef) void; +pub extern fn JSObjectGetPrivate(object: JSObjectRef) ?*c_void; +pub extern fn JSObjectSetPrivate(object: JSObjectRef, data: ?*c_void) bool; +pub extern fn JSObjectIsFunction(ctx: JSContextRef, object: JSObjectRef) bool; +pub extern fn JSObjectCallAsFunction(ctx: JSContextRef, object: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSValueRef; +pub extern fn JSObjectIsConstructor(ctx: JSContextRef, object: JSObjectRef) bool; +pub extern fn JSObjectCallAsConstructor(ctx: JSContextRef, object: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectCopyPropertyNames(ctx: JSContextRef, object: JSObjectRef) JSPropertyNameArrayRef; +pub extern fn JSPropertyNameArrayRetain(array: JSPropertyNameArrayRef) JSPropertyNameArrayRef; +pub extern fn JSPropertyNameArrayRelease(array: JSPropertyNameArrayRef) void; +pub extern fn JSPropertyNameArrayGetCount(array: JSPropertyNameArrayRef) usize; +pub extern fn JSPropertyNameArrayGetNameAtIndex(array: JSPropertyNameArrayRef, index: usize) JSStringRef; +pub extern fn JSPropertyNameAccumulatorAddName(accumulator: JSPropertyNameAccumulatorRef, propertyName: JSStringRef) void; +pub extern fn JSContextGroupCreate() JSContextGroupRef; +pub extern fn JSContextGroupRetain(group: JSContextGroupRef) JSContextGroupRef; +pub extern fn JSContextGroupRelease(group: JSContextGroupRef) void; +pub extern fn JSGlobalContextCreate(globalObjectClass: JSClassRef) JSGlobalContextRef; +pub extern fn JSGlobalContextCreateInGroup(group: JSContextGroupRef, globalObjectClass: JSClassRef) JSGlobalContextRef; +pub extern fn JSGlobalContextRetain(ctx: JSGlobalContextRef) JSGlobalContextRef; +pub extern fn JSGlobalContextRelease(ctx: JSGlobalContextRef) void; +pub extern fn JSContextGetGlobalObject(ctx: JSContextRef) JSObjectRef; +pub extern fn JSContextGetGroup(ctx: JSContextRef) JSContextGroupRef; +pub extern fn JSContextGetGlobalContext(ctx: JSContextRef) JSGlobalContextRef; +pub extern fn JSGlobalContextCopyName(ctx: JSGlobalContextRef) JSStringRef; +pub extern fn JSGlobalContextSetName(ctx: JSGlobalContextRef, name: JSStringRef) void; +pub const JSChar = c_ushort; +pub extern fn JSStringCreateWithCharacters(chars: [*c]const JSChar, numChars: usize) JSStringRef; +pub extern fn JSStringCreateWithUTF8CString(string: [*c]const u8) JSStringRef; +pub extern fn JSStringRetain(string: JSStringRef) JSStringRef; +pub extern fn JSStringRelease(string: JSStringRef) void; +pub extern fn JSStringGetLength(string: JSStringRef) usize; +pub extern fn JSStringGetCharactersPtr(string: JSStringRef) [*c]const JSChar; +pub extern fn JSStringGetMaximumUTF8CStringSize(string: JSStringRef) usize; +pub extern fn JSStringGetUTF8CString(string: JSStringRef, buffer: [*c]u8, bufferSize: usize) usize; +pub extern fn JSStringIsEqual(a: JSStringRef, b: JSStringRef) bool; +pub extern fn JSStringIsEqualToUTF8CString(a: JSStringRef, b: [*c]const u8) bool; +pub extern fn JSObjectMakeTypedArray(ctx: JSContextRef, arrayType: JSTypedArrayType, length: usize, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithBytesNoCopy(ctx: JSContextRef, arrayType: JSTypedArrayType, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithArrayBuffer(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithArrayBufferAndOffset(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, byteOffset: usize, length: usize, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectGetTypedArrayBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) ?*c_void; +pub extern fn JSObjectGetTypedArrayLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; +pub extern fn JSObjectGetTypedArrayByteLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; +pub extern fn JSObjectGetTypedArrayByteOffset(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; +pub extern fn JSObjectGetTypedArrayBuffer(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectMakeArrayBufferWithBytesNoCopy(ctx: JSContextRef, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSObjectGetArrayBufferBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) ?*c_void; +pub extern fn JSObjectGetArrayBufferByteLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; +pub extern fn JSStringCreateWithCFString(string: CFStringRef) JSStringRef; +pub const OpaqueJSContextGroup = struct_OpaqueJSContextGroup; +pub const OpaqueJSContext = struct_OpaqueJSContext; +pub const OpaqueJSString = struct_OpaqueJSString; +pub const OpaqueJSClass = struct_OpaqueJSClass; +pub const OpaqueJSPropertyNameArray = struct_OpaqueJSPropertyNameArray; +pub const OpaqueJSPropertyNameAccumulator = struct_OpaqueJSPropertyNameAccumulator; +pub const OpaqueJSValue = struct_OpaqueJSValue; + +// -- Manual -- + +// StringImpl::createWithoutCopying +// https://github.com/WebKit/webkit/blob/main/Source/JavaScriptCore/API/JSStringRef.cpp#L62 +pub extern fn JSStringCreateWithCharactersNoCopy(string: [*c]const JSChar, numChars: size_t) JSStringRef; + diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig new file mode 100644 index 000000000..aff505f22 --- /dev/null +++ b/src/javascript/jsc/javascript.zig @@ -0,0 +1,534 @@ +const js = @import("./JavaScriptCore.zig"); +const std = @import("std"); +usingnamespace @import("../../global.zig"); +const Fs = @import("../../fs.zig"); +const resolver = @import("../../resolver/resolver.zig"); +const ast = @import("../../ast/base.zig"); +const NodeModuleBundle = @import("../../node_module_bundle.zig").NodeModuleBundle; +const WTFString = @import("../../wtf_string_mutable.zig").WTFStringMutable; +const logger = @import("../../logger.zig"); +pub const ExportJavaScript = union(Tag) { + Module: *Module, + String: *String, + GlobalObject: *GlobalObject, + + pub const Tag = enum { + Module, + String, + GlobalObject, + }; +}; + +pub const ResolveFunctionType = fn (ctx: anytype, source_dir: string, import_path: string, import_kind: ast.ImportKind) anyerror!resolver.Result; +pub const TranspileFunctionType = fn (ctx: anytype, resolve_result: resolver.Result) anyerror![:0]const u8; + +const JSStringMapContext = struct { + pub fn hash(self: @This(), s: js.JSStringRef) u64 { + return hashString(s); + } + pub fn eql(self: @This(), a: js.JSStringRef, b: js.JSStringRef) bool { + return eqlString(a, b); + } +}; + +pub fn JSStringMap(comptime V: type) type { + return std.HashMap(js.JSStringRef, V, JSStringMapContext, 60); +} + +// If you read JavascriptCore/API/JSVirtualMachine.mm - https://github.com/WebKit/WebKit/blob/acff93fb303baa670c055cb24c2bad08691a01a0/Source/JavaScriptCore/API/JSVirtualMachine.mm#L101 +// We can see that it's sort of like std.mem.Allocator but for JSGlobalContextRef, to support Automatic Reference Counting +// Its unavailable on Linux +pub const VirtualMachine = struct { + const RequireCacheType = std.AutoHashMap(u64, Module); + ctx: js.JSGlobalContextRef, + group: js.JSContextGroupRef, + allocator: *std.mem.Allocator, + transpile_ctx: *c_void = undefined, + transpile: *TranspileFunctionType = undefined, + require_cache: RequireCacheType, + resolve_: *ResolveFunctionType = undefined, + resolve_ctx: *c_void = undefined, + node_modules: ?*NodeModuleBundle = null, + node_modules_ref: js.JSObjectRef = null, + global: GlobalObject, + + pub fn init(allocator: *std.mem.Allocator) !*VirtualMachine { + var group = js.JSContextGroupCreate(); + var ctx = js.JSGlobalContextCreateInGroup(group, null); + + Properties.init(); + var vm = try allocator.create(VirtualMachine); + vm.* = .{ + .allocator = allocator, + .group = group, + .ctx = ctx, + .require_cache = RequireCacheType.init(allocator), + .global = undefined, + }; + vm.global = GlobalObject{ .vm = undefined }; + return vm; + } + + pub fn setupGlobals(this: *VirtualMachine) void {} + + pub fn resolve( + this: *VirtualMachine, + from: *const Module, + to: string, + ) !js.JSValueRef { + return (try this.resolve_(this.resolve_ctx, from.path.dir, to, .require)) orelse return error.ModuleNotFound; + } + threadlocal var require_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + + // Assume bundle is already transpiled, so we skip transpiling in here. + pub fn requireFromBundle(this: *VirtualMachine, import_path: string) !js.JSValueRef {} + + pub fn require( + this: *VirtualMachine, + module: *const Module, + path_value: js.JSValueRef, + ) !js.JSValueRef { + var import_path = To.Zig.str(path_value, &require_buf); + var resolve_result = try this.resolve(module, import_path); + } + + threadlocal var eval_buf: WTFString = undefined; + threadlocal var eval_buf_loaded: bool = false; + + pub fn evalUtf8( + this: *VirtualMachine, + path_text: string, + contents: string, + ) !js.JSValueRef { + if (!eval_buf_loaded) { + eval_buf = try WTFString.init(this.allocator, contents.len + path.text.len + 2); + } else { + eval_buf.reset(); + try eval_buf.growIfNeeded(contents.len + path.text.len + 2); + } + + try eval_buf.append(contents); + eval_buf.list.append(eval_buf.allocator, 0); + var script_len = eval_buf.list.items.len; + if (path_text.len > 0) { + try eval_buf.append(path_text); + eval_buf.list.append(eval_buf.allocator, 0); + } + + var buf = eval_buf.toOwnedSliceLeaky(); + var script = js.JSStringCreateWithCharactersNoCopy(buf[0..script_len].ptr, script_len - 1); + var sourceURL: js.JSStringRef = null; + + if (path_text.len > 0) { + sourceURL = js.JSStringCreateWithCharactersNoCopy( + buf[script_len + 1 ..].ptr, + buf[script_len + 1 ..].len - 1, + ); + } + + return js.JSEvaluateScript( + this.ctx, + script, + js.JSValueMakeUndefined(this.ctx), + sourceURL, + 0, + null, + ); + } +}; + +pub const BundleLoader = struct { + bundle: *const NodeModuleBundle, + allocator: *std.mem.Allocator, + vm: *VirtualMachine, + loaded: bool = false, + ref: js.JSObjectRef = null, + + pub fn init(bundle: *const NodeModuleBundle, allocator: *std.mem.Allocator, vm: *VirtualMachine) BundleLoader { + return BundleLoader{ + .bundle = bundle, + .allocator = allocator, + .vm = vm, + }; + } + + pub fn loadBundle(this: *BundleLoader) !void {} +}; + +pub const To = struct { + pub const JS = struct { + pub inline fn str(ref: anytype, val: anytype) js.JSStringRef { + return js.JSStringCreateWithUTF8CString(val[0.. :0]); + } + + pub fn functionWithCallback( + comptime ZigContextType: type, + zig: *ZigContextType, + name: js.JSStringRef, + ctx: js.JSContextRef, + comptime callback: fn ( + obj: *ZigContextType, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []js.JSValueRef, + exception: [*c]js.JSValueRef, + ) js.JSValueRef, + ) js.JSObjectRef { + var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback)); + js.JSObjectSetPrivate(function, @ptrCast(*c_void, zig)); + return function; + } + + pub fn Callback( + comptime ZigContextType: type, + comptime ctxfn: fn ( + obj: *ZigContextType, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []js.JSValueRef, + exception: [*c]js.JSValueRef, + ) js.JSValueRef, + ) type { + return struct { + pub fn run( + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + argumentCount: usize, + arguments: [*c]const js.JSValueRef, + exception: [*c]js.JSValueRef, + ) callconv(.C) js.JSValueRef { + var object_ptr = js.JSObjectGetPrivate(function) orelse { + return js.JSValueMakeUndefined(ctx); + }; + + return ctxfn( + @intToPtr(ZigContextType, object_ptr), + ctx, + function, + thisObject, + arguments[0..argumentCount], + exception, + ); + } + }; + } + }; + + pub const Ref = struct { + pub inline fn str(ref: anytype) js.JSStringRef { + return @as(js.JSStringRef, ref); + } + }; + + pub const Zig = struct { + pub inline fn str(ref: anytype, buf: anytype) string { + return buf[0..js.JSStringGetUTF8CString(Ref.str(ref), buf.ptr, buf.len)]; + } + }; +}; + +pub const Properties = struct { + pub const UTF8 = struct { + pub const module = "module"; + pub const globalThis = "globalThis"; + pub const exports = "exports"; + pub const log = "log"; + pub const debug = "debug"; + pub const info = "info"; + pub const error_ = "error"; + pub const warn = "warn"; + pub const console = "console"; + pub const require = "require"; + pub const description = "description"; + }; + + pub const UTF16 = struct { + pub const module = std.unicode.utf8ToUtf16LeStringLiteral("module"); + pub const globalThis = std.unicode.utf8ToUtf16LeStringLiteral("globalThis"); + pub const exports = std.unicode.utf8ToUtf16LeStringLiteral("exports"); + pub const log = std.unicode.utf8ToUtf16LeStringLiteral("log"); + pub const debug = std.unicode.utf8ToUtf16LeStringLiteral("debug"); + pub const info = std.unicode.utf8ToUtf16LeStringLiteral("info"); + pub const error_ = std.unicode.utf8ToUtf16LeStringLiteral("error"); + pub const warn = std.unicode.utf8ToUtf16LeStringLiteral("warn"); + pub const console = std.unicode.utf8ToUtf16LeStringLiteral("console"); + pub const require = std.unicode.utf8ToUtf16LeStringLiteral("require"); + pub const description = std.unicode.utf8ToUtf16LeStringLiteral("description"); + }; + + pub const Refs = struct { + pub var module: js.JSStringRef = undefined; + pub var globalThis: js.JSStringRef = undefined; + pub var exports: js.JSStringRef = undefined; + pub var log: js.JSStringRef = undefined; + pub var debug: js.JSStringRef = undefined; + pub var info: js.JSStringRef = undefined; + pub var error_: js.JSStringRef = undefined; + pub var warn: js.JSStringRef = undefined; + pub var console: js.JSStringRef = undefined; + pub var require: js.JSStringRef = undefined; + pub var description: js.JSStringRef = undefined; + }; + + pub fn init() void { + inline for (std.meta.fieldNames(UTF8)) |name| { + @field(Refs, name) = js.JSStringCreateWithCharactersNoCopy( + &@field(StringStore.UTF16, name), + @field(StringStore.UTF16, name).len, + ); + } + } +}; + +pub const Object = struct { + ref: js.jsObjectRef, +}; + +pub const String = struct { + ref: js.JSStringRef, + len: usize, + + pub fn chars(this: *const String) []js.JSChar { + return js.JSStringGetCharactersPtr(this.ref)[0..js.JSStringGetLength(this.ref)]; + } + + pub fn eql(this: *const String, str: [*c]const u8) bool { + return str.len == this.len and js.JSStringIsEqualToUTF8CString(this, str); + } +}; + +pub const Module = struct { + path: Fs.PathName, + + require: RequireObject, + hash: u64, + ref: js.JSObjectRef, + + pub const RequireObject = struct {}; + + pub fn require( + this: *Module, + arguments: [*c]const js.JSValueRef, + arguments_len: usize, + exception: [*c]JSValueRef, + ) js.JSValueRef {} +}; + +pub const GlobalObject = struct { + ref: js.JSObjectRef = undefined, + vm: *VirtualMachine, + console: js.JSClassRef = undefined, + console_definition: js.JSClassDefinition = undefined, + + pub const ConsoleClass = NewSingletonClass( + GlobalObject, + "Console", + .{ + .@"log" = stdout, + .@"info" = stdout, + .@"debug" = stdout, + .@"verbose" = stdout, + + .@"error" = stderr, + .@"warn" = stderr, + }, + // people sometimes modify console.log, let them. + false, + ); + + pub fn load(global: *GlobalObject) !void { + global.console_definition = ConsoleClass.define(global, global.vm.ctx); + global.console = js.JSClassCreate(&global.console_definition); + } + + fn valuePrinter(comptime ValueType: js.JSType, ctx: js.JSContextRef, arg: js.JSValueRef, writer: anytype) !void { + switch (ValueType) { + .kJSTypeUndefined => { + try writer.writeAll("undefined"); + }, + .kJSTypeNull => { + try writer.writeAll("null"); + }, + .kJSTypeBoolean => { + if (js.JSValueToBoolean(ctx, arg)) { + try writer.writeAll("true"); + } else { + try writer.writeAll("false"); + } + }, + .kJSTypeNumber => { + try writer.print("{d}", js.JSValueToNumber(ctx, arg, null)); + }, + .kJSTypeString => { + var str = String{ .ref = @as(js.JSStringRef, arg) }; + var chars = str.chars(); + switch (chars.len) { + 0 => { + try writer.writeAll("\"\""); + }, + else => { + for (chars) |c, i| { + switch (c) { + 0...127 => { + // Since we're writing character by character + // it will be really slow if we check for an error every time + _ = writer.write(@intCast(u8, c)) catch 0; + }, + // TODO: + else => {}, + } + } + }, + } + }, + .kJSTypeObject => { + // TODO: + try writer.writeAll("[Object object]"); + }, + .kJSTypeSymbol => { + var description = js.JSObjectGetPropertyForKey(ctx, arg, Properties.Refs.description, null); + return switch (js.JSValueGetType(ctx, description)) { + .kJSTypeString => try valuePrinter(.kJSTypeString, ctx, description, writer), + else => try valuePrinter(.kJSTypeUndefined, ctx, description, writer), + }; + }, + else => {}, + } + } + + fn output( + writer: anytype, + ctx: js.JSContextRef, + arguments: []js.JSValueRef, + ) !void { + defer Output.flush(); + // console.log(); + if (arguments.len == 0) { + return js.JSValueMakeUndefined(ctx); + } + + const last = arguments.len - 1; + + for (arguments) |arg, i| { + switch (js.JSValueGetType(ctx, arg)) { + .kJSTypeUndefined => { + try valuePrinter(.kJSTypeUndefined, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeNull => { + try valuePrinter(.kJSTypeNull, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeBoolean => { + try valuePrinter(.kJSTypeBoolean, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeNumber => { + try valuePrinter(.kJSTypeNumber, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeString => { + try valuePrinter(.kJSTypeString, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeObject => { + try valuePrinter(.kJSTypeObject, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + .kJSTypeSymbol => { + try valuePrinter(.kJSTypeSymbol, ctx, arg, writer); + if (i != last) { + try writer.writeAll(" "); + } + }, + else => {}, + } + } + + return js.JSValueMakeUndefined(ctx); + } + + pub fn stdout( + obj: *GlobalObject, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []js.JSValueRef, + ) js.JSValueRef { + return try output(Output.writer(), ctx, arguments); + } + + pub fn stderr( + obj: *GlobalObject, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []js.JSValueRef, + ) js.JSValueRef { + return try output(Output.errorWriter(), ctx, arguments); + // js.JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) + } +}; + +pub fn NewSingletonClass( + comptime ZigType: type, + comptime name: string, + comptime functions: anytype, + comptime read_only: bool, +) type { + return struct { + const ClassDefinitionCreator = @This(); + const function_names = std.meta.fieldNames(functions); + const function_name_literals: [function_names.len][]js.JSChar = brk: { + var names = std.mem.zeroes([function_names.len][]js.JSChar); + + for (function_names) |field, i| { + names[i] = std.unicode.utf8ToUtf16LeStringLiteral(field); + } + break :brk names; + }; + var function_name_refs: [function_names.len]js.JSStringRef = undefined; + + const class_name_literal = std.unicode.utf8ToUtf16LeStringLiteral(name); + var static_functions: [function_name_refs.len + 1:0]js.JSStaticFunction = undefined; + + pub fn define(zig: *ZigType, ctx: js.JSContextRef) !js.JSClassDefinition { + var def = std.mem.zeroes(js.JSClassDefinition); + + inline for (function_name_literals) |function_name, i| { + function_name_refs[i] = js.JSStringCreateWithCharactersNoCopy(&function_name, function_name.len); + static_functions[i] = js.JSStaticFunction{ + .name = (function_names[i][0.. :0]).ptr, + .callAsFunction = To.JS.functionWithCallback( + ZigType, + zig, + function_name_refs[i], + ctx, + @field(functions, function_names[i]), + ), + .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, + }; + } + static_functions[function_name_literals.len] = std.mem.zeroes(js.JSStaticFunction); + def.staticFunctions = static_functions[0.. :0].ptr; + def.className = name[0.. :0].ptr; + + return def; + } + }; +} diff --git a/src/js_lexer.zig b/src/js_lexer.zig index c71b6b628..0e469122e 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -2625,31 +2625,28 @@ pub const CodepointIterator = struct { width: u3 = 0, c: CodePoint = 0, - pub fn nextCodepointSlice(it: *CodepointIterator) ?[]const u8 { - if (it.i >= it.bytes.len) { - return null; - } + pub fn nextCodepointSlice(it: *CodepointIterator) []const u8 { + @setRuntimeSafety(false); - const cp_len = std - .unicode.utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable; + const cp_len = strings.utf8ByteSequenceLength(it.bytes[it.i]); it.i += cp_len; - return it.bytes[it.i - cp_len .. it.i]; + + return if (!(it.i > it.bytes.len)) it.bytes[it.i - cp_len .. it.i] else ""; } pub fn nextCodepoint(it: *CodepointIterator) ?CodePoint { - const slice = it.nextCodepointSlice() orelse return null; - it.width = @intCast(u3, slice.len); - @setRuntimeSafety(false); + const slice = it.nextCodepointSlice(); - it.c = switch (it.width) { - 1 => @intCast(CodePoint, slice[0]), - 2 => @intCast(CodePoint, std.unicode.utf8Decode2(slice) catch unreachable), - 3 => @intCast(CodePoint, std.unicode.utf8Decode3(slice) catch unreachable), - 4 => @intCast(CodePoint, std.unicode.utf8Decode4(slice) catch unreachable), + it.c = switch (slice.len) { + 0 => it.c, + 1 => @as(CodePoint, slice[0]), + 2 => @as(CodePoint, unicode.utf8Decode2(slice) catch unreachable), + 3 => @as(CodePoint, unicode.utf8Decode3(slice) catch unreachable), + 4 => @as(CodePoint, unicode.utf8Decode4(slice) catch unreachable), else => unreachable, }; - return it.c; + return if (slice.len > 0) it.c else null; } /// Look ahead at the next n codepoints without advancing the iterator. diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index f8b565fe7..39c3af492 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -2259,7 +2259,6 @@ const ParserFeatures = struct { // // // - react_fast_refresh: bool = false, }; diff --git a/src/options.zig b/src/options.zig index 54483e801..aca85ed31 100644 --- a/src/options.zig +++ b/src/options.zig @@ -244,9 +244,10 @@ pub const ModuleType = enum { }; pub const Platform = enum { - node, - browser, neutral, + browser, + speedy, + node, pub const Extensions = struct { pub const In = struct { diff --git a/src/runtime.footer.js b/src/runtime.footer.js index 94a358eb5..cad687e2d 100644 --- a/src/runtime.footer.js +++ b/src/runtime.footer.js @@ -1,6 +1,8 @@ // --- // Public exports from runtime -export var $$m = SPEEDY_RUNTIME.$$m; +// Compatible with Speedy's Runtime Environment and web browsers. +export var $$m = + "$primordials" in globalThis ? $primordials.require : SPEEDY_RUNTIME.$$m; export var __HMRModule = SPEEDY_RUNTIME.__HMRModule; export var __FastRefreshModule = SPEEDY_RUNTIME.__FastRefreshModule; export var __HMRClient = SPEEDY_RUNTIME.__HMRClient; @@ -9,6 +11,6 @@ export var $$lzy = SPEEDY_RUNTIME.$$lzy; export var __toModule = SPEEDY_RUNTIME.__toModule; export var __commonJS = SPEEDY_RUNTIME.__commonJS; export var __require = SPEEDY_RUNTIME.__require; -export var __name = SPEEDY_RUNTIME.__name; -export var __export = SPEEDY_RUNTIME.__export; -export var __reExport = SPEEDY_RUNTIME.__reExport; +export var __name = PEEDY_RUNTIME.__name; +export var __export = PEEDY_RUNTIME.__export; +export var __reExport = PEEDY_RUNTIME.__reExport; diff --git a/src/runtime.version b/src/runtime.version index 38984f56a..eaedfa1e4 100644 --- a/src/runtime.version +++ b/src/runtime.version @@ -1 +1 @@ -2a14be7fb8890d70
\ No newline at end of file +35bcb0f8b5e80d25
\ No newline at end of file diff --git a/src/string_immutable.zig b/src/string_immutable.zig index c97b0901e..546ba8a4f 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -305,12 +305,17 @@ pub fn encodeWTF8Rune(p: []u8, r: i32) u3 { } pub fn toUTF16Buf(in: string, out: []u16) usize { - var utf8Iterator = std.unicode.Utf8Iterator{ .bytes = in, .i = 0 }; + var utf8Iterator = CodepointIterator{ .bytes = in, .i = 0 }; var c: u21 = 0; var i: usize = 0; - while (utf8Iterator.nextCodepoint()) |code_point| { + while (true) { + const code_point = utf8Iterator.nextCodepoint(); + switch (code_point) { + -1 => { + return i; + }, 0...0xFFFF => { out[i] = @intCast(u16, code_point); i += 1; @@ -329,7 +334,7 @@ pub fn toUTF16Buf(in: string, out: []u16) usize { } pub fn toUTF16Alloc(in: string, allocator: *std.mem.Allocator) !JavascriptString { - var utf8Iterator = std.unicode.Utf8Iterator{ .bytes = in, .i = 0 }; + var utf8Iterator = CodepointIterator{ .bytes = in, .i = 0 }; var out = try std.ArrayList(u16).initCapacity(allocator, in.len); var c: u21 = 0; diff --git a/src/wtf_string_mutable.zig b/src/wtf_string_mutable.zig new file mode 100644 index 000000000..2c94dae22 --- /dev/null +++ b/src/wtf_string_mutable.zig @@ -0,0 +1,243 @@ +const std = @import("std"); +const expect = std.testing.expect; + +usingnamespace @import("string_types.zig"); +const strings = @import("string_immutable.zig"); +const js_lexer = @import("js_lexer.zig"); +const ListType = std.ArrayListUnmanaged(u16); +pub const WTFStringMutable = struct { + allocator: *std.mem.Allocator, + list: ListType, + + pub const Writer = std.io.Writer(*@This(), anyerror, WTFStringMutable.writeAll); + pub fn writer(self: *WTFStringMutable) Writer { + return Writer{ + .context = self, + }; + } + + pub fn deinit(str: *WTFStringMutable) void { + str.list.deinit(str.allocator); + } + + pub fn growIfNeeded(self: *WTFStringMutable, amount: usize) !void { + try self.list.ensureUnusedCapacity(self.allocator, amount); + } + + pub fn write(self: *WTFStringMutable, bytes: anytype) !usize { + try self.list.appendSlice(self.allocator, bytes); + return bytes.len; + } + + pub fn writeAll(self: *WTFStringMutable, bytes: string) !usize { + try self.list.appendSlice(self.allocator, bytes); + return self.list.items.len; + } + + pub fn init(allocator: *std.mem.Allocator, capacity: usize) !WTFStringMutable { + return WTFStringMutable{ .allocator = allocator, .list = try ListType.initCapacity(allocator, capacity) }; + } + + pub fn initCopy(allocator: *std.mem.Allocator, str: anytype) !WTFStringMutable { + var mutable = try WTFStringMutable.init(allocator, std.mem.len(str)); + try mutable.copy(str); + return mutable; + } + + // Convert it to an ASCII identifier. Note: If you change this to a non-ASCII + // identifier, you're going to potentially cause trouble with non-BMP code + // points in target environments that don't support bracketed Unicode escapes. + + pub fn ensureValidIdentifier(str: string, allocator: *std.mem.Allocator) !string { + if (str.len == 0) { + return "_"; + } + + var has_needed_gap = false; + var needs_gap = false; + var start_i: usize = 0; + + // Common case: no gap necessary. No allocation necessary. + needs_gap = !js_lexer.isIdentifierStart(@intCast(js_lexer.CodePoint, str[0])); + if (!needs_gap) { + // Are there any non-alphanumeric chars at all? + for (str[1..str.len]) |c, i| { + if (!js_lexer.isIdentifierContinue(@intCast(js_lexer.CodePoint, c))) { + needs_gap = true; + start_i = 1 + i; + break; + } + } + } + + if (needs_gap) { + var mutable = try WTFStringMutable.initCopy(allocator, str[0..start_i]); + needs_gap = false; + + var i: usize = 0; + + var slice = str[start_i..]; + + while (i < slice.len) : (i += 1) { + const c = @intCast(js_lexer.CodePoint, slice[i]); + if (js_lexer.isIdentifierContinue(c)) { + if (needs_gap) { + try mutable.appendChar('_'); + needs_gap = false; + has_needed_gap = true; + } + + try mutable.appendChar(slice[i]); + } else if (!needs_gap) { + needs_gap = true; + // skip the code point, replace it with a single _ + i += std.math.max(strings.utf8ByteSequenceLength(slice[i]), 1) - 1; + } + } + + // If it ends with an emoji + if (needs_gap) { + try mutable.appendChar('_'); + needs_gap = false; + has_needed_gap = true; + } + + return mutable.list.toOwnedSlice(allocator); + } + + return str; + } + + pub fn len(self: *const WTFStringMutable) usize { + return self.list.items.len; + } + + pub fn copy(self: *WTFStringMutable, str: anytype) !void { + try self.list.ensureCapacity(self.allocator, std.mem.len(str[0..])); + + if (self.list.items.len == 0) { + try self.list.insertSlice(self.allocator, 0, str); + } else { + try self.list.replaceRange(self.allocator, 0, std.mem.len(str[0..]), str[0..]); + } + } + + pub inline fn growBy(self: *WTFStringMutable, amount: usize) !void { + try self.list.ensureUnusedCapacity(self.allocator, amount); + } + + pub inline fn reset( + self: *WTFStringMutable, + ) void { + self.list.shrinkRetainingCapacity(0); + } + + pub inline fn appendChar(self: *WTFStringMutable, char: u8) !void { + try self.list.append(self.allocator, char); + } + pub inline fn appendCharAssumeCapacity(self: *WTFStringMutable, char: u8) void { + self.list.appendAssumeCapacity(char); + } + pub inline fn append(self: *WTFStringMutable, str: []const u8) !void { + self.growIfNeeded(str.len); + + var iter = strings.CodepointIterator{ .bytes = str, .i = 0 }; + while (true) { + switch (iter.nextCodepoint()) { + -1 => { + return; + }, + 0...0xFFFF => { + try self.list.append(@intCast(u16, iter.c)); + }, + else => { + const c = iter.c - 0x10000; + try self.list.append(@intCast(u16, 0xD800 + ((c >> 10) & 0x3FF))); + try self.list.append(@intCast(u16, 0xDC00 + (c & 0x3FF))); + }, + } + } + } + + pub inline fn appendComptimeConvert(self: *WTFStringMutable, comptime _str: []const u8) !void { + const str = std.unicode.utf8ToUtf16LeStringLiteral(_str); + try self.list.appendSlice(str); + } + + pub inline fn appendAssumeCapacity(self: *WTFStringMutable, str: []const u8) void { + var iter = strings.CodepointIterator{ .bytes = str, .i = 0 }; + while (true) { + switch (iter.nextCodepoint()) { + -1 => { + return; + }, + 0...0xFFFF => { + self.list.appendAssumeCapacity(@intCast(u16, iter.c)); + }, + else => { + const c = iter.c - 0x10000; + self.list.appendAssumeCapacity(@intCast(u16, 0xD800 + ((c >> 10) & 0x3FF))); + self.list.appendAssumeCapacity(@intCast(u16, 0xDC00 + (c & 0x3FF))); + }, + } + } + } + pub inline fn lenI(self: *WTFStringMutable) i32 { + return @intCast(i32, self.list.items.len); + } + + pub fn toOwnedSlice(self: *WTFStringMutable) string { + return self.list.toOwnedSlice(self.allocator); + } + + pub fn toOwnedSliceLeaky(self: *WTFStringMutable) string { + return self.list.items; + } + + pub fn toOwnedSliceLength(self: *WTFStringMutable, length: usize) string { + self.list.shrinkAndFree(self.allocator, length); + return self.list.toOwnedSlice(self.allocator); + } + + // pub fn deleteAt(self: *WTFStringMutable, i: usize) { + // self.list.swapRemove(i); + // } + + pub fn containsChar(self: *WTFStringMutable, char: u8) bool { + return self.indexOfChar(char) != null; + } + + pub fn indexOfChar(self: *WTFStringMutable, char: u8) ?usize { + return std.mem.indexOfScalar(@TypeOf(char), self.list.items, char); + } + + pub fn lastIndexOfChar(self: *WTFStringMutable, char: u8) ?usize { + return std.mem.lastIndexOfScalar(@TypeOf(char), self.list.items, char); + } + + pub fn lastIndexOf(self: *WTFStringMutable, str: u8) ?usize { + return std.mem.lastIndexOf(u8, self.list.items, str); + } + + pub fn indexOf(self: *WTFStringMutable, str: u8) ?usize { + return std.mem.indexOf(u8, self.list.items, str); + } + + pub fn eql(self: *WTFStringMutable, other: anytype) bool { + return std.mem.eql(u8, self.list.items, other); + } +}; + +test "WTFStringMutable" { + const alloc = std.heap.page_allocator; + + var str = try WTFStringMutable.initCopy(alloc, "hello"); + expect(str.eql("hello")); +} + +test "WTFStringMutable.ensureValidIdentifier" { + const alloc = std.heap.page_allocator; + + try std.testing.expectEqualStrings("jquery", try WTFStringMutable.ensureValidIdentifier("jquery", alloc)); + try std.testing.expectEqualStrings("jquery_foo", try WTFStringMutable.ensureValidIdentifier("jquery😋foo", alloc)); +} |