diff --git a/JS-API-Documentation.html b/JS-API-Documentation.html new file mode 100644 index 000000000..22c4be178 --- /dev/null +++ b/JS-API-Documentation.html @@ -0,0 +1,2071 @@ + + + + +Project64 JavaScript API 2021.09.02 + + + + + + +
+ +
+
Project64 JavaScript API
+ +

Required settings
+

+

The following settings must be used to enable the script console.

+

Go to Options > Configuration and uncheck Hide advanced settings. +Optional: You may also want to uncheck Pause emulation when window is not active for general debugging.

+

+

+

Click Advanced, then check Enable Debugger. +Optional: Check Always use interpreter core -- +This is required for some of the events interface functions to work, but it may impact game performance.

+

+ +

Script console
+

+

Go to Debugger > Scripts... to open the script console.

+

+

+

You can view the scripts directory in the file explorer by clicking the ... button. +JavaScript (*.js) files in the root of this directory are visible to the console.

+

A script may be toggled by double clicking it, or selecting it and then clicking the Run/Stop button.

+

While a script is active you may select it and enter code in the input box at the bottom of of the window. +Entered code is evaluated in the selected script's instance (namespace is not shared between instances). +You can use the up and down arrow keys to scroll through input history.

+

You may configure a script to run automatically when Project64 is started by right clicking it and clicking Autorun...

+

+ +

JavaScript environment
+

+

Project64's script system uses the Duktape JavaScript engine. Duktape supports ECMAScript E5 and some features from ES2015+.

+ + +
+ +
+
console
+Console interface + +

console.log(message[, ...])
+
console.log(message: any, ...optionalParams): void
+

+Prints all provided arguments to the console with a trailing newline. +
+console.log("Hello", "world!");
+
+ +

console.print(message[, ...])
+
console.print(message: any, ...optionalParams): void
+

+Prints all provided arguments to the console. +
+console.print("Hello ");
+console.print("world!\n");
+
+ +

console.error(err)
+
console.print(err: Error): void
+

+Prints the stack property of an error object. + +

console.clear()
+
console.clear(): void
+

+Clears the console. + +

console.listen(inputListener)
+
console.listen(inputListener: null | (input: string) => void): void
+

+

Overrides the default input behavior; console input will be passed to inputListener instead of being evaluated as JS code.

+ +

The listener may be unset by calling console.listen(null).

+
+console.listen(function(input) {
+    var args = input.split(" ");
+    var command = args.shift();
+    switch(command) {
+    case "ping":
+        console.log("pong (args were:", args.join(", "), ")");
+        return;
+    case "end":
+        console.log("done");
+        console.listen(null);
+        return;
+    default:
+        console.log("unknown command '" + command + "'");
+    }
+});
+
+ +
+ +
+
pj64
+Project64 interface + +

pj64.open(romPath)
+
pj64.open(romPath: string): void
+

+Opens a ROM. + +

pj64.close()
+
pj64.close(): void
+

+Closes the current ROM. + +

pj64.reset([soft])
+
pj64.reset(soft?: boolean = false): void
+

+Resets emulation. Performs a soft reset if soft is true. + +

pj64.pause()
+
pj64.pause(): void
+

+Pauses emulation. + +

pj64.resume()
+
pj64.resume(): void
+

+Resumes emulation. + +

pj64.limitfps(limitFps)
+
pj64.limitfps(limitFps: boolean)
+

+Enables or disables the FPS limit. + +

pj64.installDirectory
+
pj64.installDirectory: string
+

+Path to the directory containing the Project64 executable. Read-only. + +

pj64.scriptsDirectory
+
pj64.scriptsDirectory: string
+

+Path to the scripts directory. Read-only. + +

pj64.modulesDirectory
+
pj64.modulesDirectory: string
+

+Path to the script modules directory. Read-only. + +

pj64.romDirectory
+
pj64.romDirectory: string
+

+

Path to the current ROM directory. Read-only.

+ +

pj64.romInfo
+
pj64.romInfo: null | Object
+

+

Object containing information about the current ROM. null if no ROM is loaded.

+ + + + + + + + + + + +
pj64.romInfo.goodName RDB "Good Name".
pj64.romInfo.fileName Name of the ROM file including the file extension.
pj64.romInfo.filePath Path to the ROM file.
pj64.romInfo.crc1 Internal CRC 1.
pj64.romInfo.crc2 Internal CRC 2.
pj64.romInfo.name Internal name.
pj64.romInfo.mediaFormat Internal media format number.
pj64.romInfo.id Internal ID character pair.
pj64.romInfo.countryCode Internal country code character.
pj64.romInfo.version Internal version number.
+ +
+ +
+
mem
+N64 memory interface + +

mem.u8|u16|u32|s8|s16|s32|f32|f64
+

+Arrays for reading and writing memory. Virtual addresses are used as indices. +
+// Read some data from ROM and RAM
+var firstWordInROM = mem.u32[0xB0000000];
+var firstWordInRAM = mem.u32[0x80000000];
+
+
+// Move player to 0,0,0 (SM64 US)
+const pPlayerX = 0x8033B1AC;
+const pPlayerY = 0x8033B1B0;
+const pPlayerZ = 0x8033B1B4;
+
+mem.f32[pPlayerX] = 0;
+mem.f32[pPlayerY] = 0;
+mem.f32[pPlayerZ] = 0;
+
+ +

mem.bindvar(object, address, name, typeId)
+
mem.bindvar(object: Object, address: number, name: string, typeId: number): void
+

+Adds a memory-bound property to object. +Valid types are u8, u16, u32, s8, s16, s32, f32, f64. +See Type IDs for more details. +
+mem.bindvar(global, 0x8033B1AC, 'playerX', f32);
+mem.bindvar(global, 0x8033B1B0, 'playerY', f32);
+mem.bindvar(global, 0x8033B1B4, 'playerZ', f32);
+
+console.log("Player's coordinates are ", playerX, playerY, playerZ);
+
+ +

mem.bindvars(object, vars)
+
mem.bindvars(object: Object, vars: any[]): Object
+

+

Adds multiple memory-bound properties to object. +vars must be an array in which each item is an array containing an address, name, and type ID (in order) describing a variable.

+ +

Returns object.

+
+mem.bindvars(global, [
+    [ 0x8033B1AC, 'playerX', f32 ],
+    [ 0x8033B1B0, 'playerY', f32 ],
+    [ 0x8033B1B4, 'playerZ', f32 ]
+]);
+
+console.log("Player's coordinates are ", playerX, playerY, playerZ);
+
+ +

mem.bindstruct(object, address, properties)
+
mem.bindstruct(object: Object, address: number, properties: Object): Object
+

+

Adds multiple memory-bound properties to object. +properties must be an object in which each key/value pair specifies the name and type ID of a variable.

+ +

Returns object.

+
+var playerPos = mem.bindstruct({}, 0x8033B1AC, { x: f32, y: f32, z: f32 });
+console.log("Player's coordinates are ", playerPos.x, playerPos.y, playerPos.z);
+
+ +

mem.typedef(properties)
+
mem.typedef(properties: Object): MemTypeConstructor
+

+Returns a constructor function that will create an object representing a structure in memory, given its address. +
+const vec3f = mem.typedef({ x: f32, y: f32, z: f32 });
+var playerAngle = new vec3f(0x8033B19C);
+var playerPos = new vec3f(0x8033B1AC);
+
+ +

mem.getblock(address, length)
+
mem.getblock(address: number, length: number): Buffer
+

+Returns a block of data from memory as a Buffer object. +
+fs.writefile("ram_dump.bin", mem.getblock(K0BASE, mem.ramSize));
+
+ +

mem.setblock(address, data[, length])
+
mem.setblock(address: number, data: string | Buffer | TypedArray, length: number): Buffer
+

+Writes a block of data into memory. data may be one of the following types: +ArrayBuffer, +Buffer, +DataView, +TypedArray, string. +
+mem.setblock(0x80400000, fs.readfile("textures.bin"));
+
+ +

mem.getstring(address[, length])
+
mem.getblock(address: number, length?: number): string
+

+Returns a zero-terminated ASCII string from memory. +
+const romName = mem.getstring(0xB0000020, 0x14);
+
+ +

mem.setstring(address, data[, length])
+
mem.setstring(address: number, data: string | Buffer | TypedArray, length?: number): Buffer
+

+Writes a string into memory (alias of mem.setblock). + +

mem.ramSize
+
mem.ramSize: number
+

+The size of RDRAM in bytes. 0 if RDRAM is not initialized. + +

mem.romSize
+
mem.romSize: number
+

+The size of cartridge ROM in bytes. 0 if ROM is not initialized. + +

mem.ptr
+
mem.ptr: pointer
+

+Native pointer to Project64's N64 memory buffer. May be used by native modules for faster memory access. +
+// C
+duk_ret_t example(duk_context* ctx)
+{
+    uint8_t* memory = NULL;
+    duk_get_global_string(ctx, "mem");
+    duk_get_prop_string(ctx, -1, "ptr");
+    memory = duk_get_pointer(ctx, -1);
+    duk_pop_n(ctx, 3);
+    // do something to memory here
+    return 0;
+}
+
+ +
+ +
+
events
+Event hooking interface + +

events.onstatechange(callback)
+
events.onstatechange(callback: (e: EmuStateChangeEvent) => void): number
+

+Registers a callback that is invoked when the state of the emulator changes. +callback receives an EmuStateChangeEvent object. +

Returns a callback ID.

+ +

EmuStateChangeEvent
+

+

+ + + +
e.callbackIdID of the callback associated with the event.
e.stateState number. See table below.
+

+ + + + + + + + + + + +
EMU_STARTEDEmulation started.
EMU_STOPPEDEmulation stopped.
EMU_PAUSEDEmulation paused.
EMU_RESUMEDEmulation resumed from a pause.
EMU_RESETTINGEmulation is resetting.
EMU_RESETEmulation reset.
EMU_LOADED_ROMA ROM or 64DD image has been loaded.
EMU_DEBUG_PAUSEDEmulation paused via debugger break.
EMU_DEBUG_RESUMEDEmulation resumed via the debugger.
+ +

events.onexec(address, callback)
Requires interpreter core
+
events.onexec(address: number | AddressRange, callback: (e: CPUExecEvent) => void): number
+

+Registers a callback that is invoked at the beginning of a CPU step if the program counter is at address. +address may be a single address or an object containing start and end address properties (e.g. AddressRange). +callback receives a CPUExecEvent object. +

Returns a callback ID.

+
+events.onexec(0x802CB1C0, function() {
+    console.log("func_802CB1C0 was called");
+});
+
+
+// Log every CPU step
+events.onexec(ADDR_ANY, function(e) {
+    console.log(e.pc.hex() + ": " + asm.decode(mem.u32[e.pc], e.pc));
+});
+
+ +

CPUExecEvent
+

+ + + +
e.callbackIdID of the callback associated with the event.
e.pcProgram counter address.
+ +

events.onread(address, callback)
Requires interpreter core
+
events.onread(address: number | AddressRange, callback: (e: CPUReadWriteEvent) => void): number
+

+Registers a callback that is invoked at the beginning of a CPU step if the CPU is going to read from address. +address may be a single address or an object containing start and end address properties (e.g. AddressRange). +callback receives a CPUReadWriteEvent object. +

Returns a callback ID.

+ +

events.onwrite(address, callback)
Requires interpreter core
+
events.onwrite(address: number | AddressRange, callback: (e: CPUReadWriteEvent) => void): number
+

+Registers a callback that is invoked at the beginning of a CPU step if the CPU is going to write to address. +address may be a single address or an object containing start and end address properties (e.g. AddressRange). +callback receives a CPUReadWriteEvent object. +

Returns a callback ID.

+
+events.onwrite(PI_WR_LEN_REG, function(e) {
+    var romAddr = mem.u32[PI_CART_ADDR_REG] + K1BASE;
+    var ramAddr = mem.u32[PI_DRAM_ADDR_REG] + K0BASE;
+    var length = e.value + 1;
+    console.log("Loaded " + romAddr.hex() + " -> " + ramAddr.hex() + " (Length: " + length.hex() + ")");
+});
+
+ +

CPUReadWriteEvent
+

+

+ + + + + + + + + +
e.callbackIdID of the callback associated with the event.
e.pcProgram counter address.
e.addressAddress that the CPU is going to read/write.
e.fputrue if the source/destination register is on the floating point unit.
e.regIndex of the source/destination register.
e.valueTypeThe value's type ID. Varies depending on the opcode; refer to the table below.
e.valueValue that the CPU is going to read/write.
e.valueHiUpper 32 bits of the value if e.valueType is u64; otherwise undefined.
+

+

+ + + + + + + + + + + +
e.typeOpcode(s)
s8LB, SB
u8LBU
s16LH, SH
u16LHU
s32LL, LW, LWL, LWR, SW, SWL, SWR
u32LWU
u64LD, LDL, LDR, SD, SDL, SDR
f32LWC1, SWC1
f64LDC1, SDC1
+

+ +

events.onopcode(address, opcode, mask, callback)
Requires interpreter core
+
events.onopcode(address: number | AddressRange, opcode: number, mask: number, callback: (e: CPUOpcodeEvent) => void): number
+

+Registers a callback that is invoked at the beginning of a CPU step if the program counter is at address and +opcode is equal to the current opcode ANDed with mask. +callback receives a CPUOpcodeEvent object. +

Returns a callback ID.

+
+// Log every JAL
+const JAL = asm.encode("jal 0");
+const ANY_TARGET = 0xFC000000; // Mask off target field
+events.onopcode(ADDR_ANY, JAL, ANY_TARGET, function(e) {
+    console.log(e.pc.hex() + ": " + asm.decode(mem.u32[e.pc], e.pc));
+});
+
+ +

CPUOpcodeEvent
+

+ + + + +
e.callbackIdID of the callback associated with the event.
e.pcProgram counter address.
e.opcodeThe opcode.
+ +

events.ongprvalue(address, regFlags, value, callback)
Requires interpreter core
+
events.ongprvalue(address: number | AddressRange, regFlags: number, value: number, callback: (e: CPURegValueEvent) => void): number
+

+Registers a callback that is invoked at the beginning of a CPU step if the program counter is at address and at least one of +the general purpose registers specified by regFlags is equal to value (lower 32 bits only). +callback receives a CPURegValueEvent object. +

Returns a callback ID.

+

See General purpose register flags for a list of valid flags. Multiple registers may be specified via bitwise OR.

+
+// Break when any general purpose register contains 0x49533634
+const IS64_SIGNATURE = 0x49533634;
+events.ongprvalue(ADDR_ANY, GPR_ANY, IS64_SIGNATURE, function() {
+    debug.breakhere();
+});
+
+ +

General purpose register flags
+

+Global flags for events.ongprvalue's regFlags parameter. +

GPR_ANY

+

GPR_R0 GPR_AT GPR_V0 GPR_V1 GPR_A0 GPR_A1 GPR_A2 GPR_A3

+

GPR_T0 GPR_T1 GPR_T2 GPR_T3 GPR_T4 GPR_T5 GPR_T6 GPR_T7

+

GPR_S0 GPR_S1 GPR_S2 GPR_S3 GPR_S4 GPR_S5 GPR_S6 GPR_S7

+

GPR_T8 GPR_T9 GPR_K0 GPR_K1 GPR_GP GPR_SP GPR_FP GPR_RA

+ +

CPURegValueEvent
+

+ + + + + +
e.callbackIdID of the callback associated with the event.
e.pcProgram counter address.
e.valueThe value.
e.regIndex of the register containing the value.
+ +

events.onpifread(callback)
+
events.onpifread(callback: (e: GenericEvent) => void): number
+

+Registers a callback that is invoked when PIF RAM data is going to be read into RDRAM. +callback receives a GenericEvent object. +

Returns a callback ID.

+

Useful for monitoring and overriding controller input.

+
+// Log P1's controller input
+events.onpifread(function() {
+    if(mem.u32[PIF_RAM_START + 0x00] == 0xFF010401) {
+        console.log(mem.u32[PIF_RAM_START + 0x04].hex());
+    }
+});
+
+ +
+// Play an M64 TAS file
+var m64file = fs.readfile("sm64-1key.m64");
+var numSamples = m64file.readUInt32LE(0x018);
+var sampleIndex = 0;
+
+events.onpifread(function() {
+    for(var nController = 0; nController < 4; nController++) {
+        var cmdAddr = PIF_RAM_START + (nController * 8);
+        if(mem.u32[cmdAddr + 0x00] == 0xFF010401 && sampleIndex < numSamples) {
+            mem.u32[cmdAddr + 0x04] = m64file.readUInt32BE(0x400 + sampleIndex * 4);
+            sampleIndex++;
+        }
+    }
+});
+
+ +

GenericEvent
+

+ + +
e.callbackIdID of the callback associated with the event.
+ +

events.onsptask(callback)
+
events.onsptask(callback: (e: SPTaskEvent) => void): number
+

+Registers a callback that is invoked when a task is sent to the RSP. +callback receives an SPTaskEvent object. +

Returns a callback ID.

+

Useful for monitoring and overriding display lists and audio lists.

+
+events.onsptask(function(e) {
+    if (e.taskType == M_GFXTASK) {
+        console.log("Display list address: " + e.dataAddress);
+    }
+    else if (e.taskType == M_AUDTASK) {
+        console.log("Audio list address: " + e.dataAddress);
+    }
+});
+
+ +

SPTaskEvent
+

+ + + + + + + + + + + + + + + + + + +
e.callbackId ID of the callback associated with the event.
e.taskType The task type. M_GFXTASK (1) for graphics tasks, or M_AUDTASK (2) for audio tasks.
e.taskFlags Task flags.
e.ucodeBootAddress Address of the boot microcode.
e.ucodeBootSize Size of the boot microcode.
e.ucodeAddress Address of the task microcode.
e.ucodeSize Size of the task microcode.
e.ucodeDataAddress Address of the microcode data.
e.ucodeDataSize Size of the microcode data.
e.dramStackAddress Address of the DRAM matrix stack.
e.dramStackSize Size of the DRAM matrix stack.
e.outputBuffAddressDP command buffer address for "_dram" and "_fifo" graphics microcodes.
e.outputBuffSize DP command buffer size for "_dram" and "_fifo" graphics microcodes.
e.dataAddress Address of the display list or audio list.
e.dataSize Size of the display list or audio list.
e.yieldDataAddress Address of the yield data buffer.
e.yieldDataSize Size of the yield data buffer.
+

Note: The properties of this object differ slightly from those in the DMEM OSTask structure; +all physical addresses are converted to KSEG0 addresses.

+ +

events.onpidma(callback)
+
events.onpidma(callback: (e: PIEvent) => void)
+

+Registers a callback that is invoked when a PI DMA transfer is going to occur. +callback receives a PIEvent object. +

Returns a callback ID.

+
+// Log all cartridge <-> RDRAM data transfers
+events.onpidma(function(e) {
+    var dir = e.direction == OS_READ ? 'READ' : 'WRITE';
+    console.log('[PI]', dir, e.dramAddress.hex(), e.cartAddress.hex(), e.length.hex());
+});
+
+ +

PIEvent
+

+ + + + + + +
e.callbackId The ID of the callback associated with the event.
e.direction The direction of the DMA transfer. May be OS_READ (0), or OS_WRITE (1).
e.dramAddress The address in PI_DRAM_ADDR_REG (+K0BASE).
e.cartAddress The address in PI_CART_ADDR_REG (+K1BASE).
e.length The value of PI_RD_LEN_REG or PI_WR_LEN_REG (+1), depending on the transfer direction.
+ +

events.onmousedown(callback)
+
events.onmousedown(callback: (e: MouseEvent) => void): number
+

+Registers a callback that is invoked when a mouse button is pressed over the main screen. +callback receives a MouseEvent object. + +

Returns a callback ID.

+ +

events.onmouseup(callback)
+
events.onmouseup(callback: (e: MouseEvent) => void): number
+

+Registers a callback that is invoked when a mouse button is released over the main screen. +callback receives a MouseEvent object. +

Returns a callback ID.

+ +

events.onmousemove(callback)
+
events.onmousemove(callback: (e: MouseEvent) => void): number
+

+Registers a callback that is invoked when the mouse is moved over the main screen. +callback receives a MouseEvent object. +

Returns a callback ID.

+ +

MouseEvent
+

+

+ + + + + +
e.callbackIdID of the callback associated with the event.
e.buttonThe mouse button number. See table below.
e.xCursor X position.
e.yCursor Y position.
+

+

+ + + + + +
MouseEvent.LEFT0
MouseEvent.MIDDLE1
MouseEvent.RIGHT2
MouseEvent.NONE-1
+

+ +

events.remove(callbackId)
+
events.remove(callbackId: number): void
+

+Unregisters the callback specified by callbackId. +
+// This callback will only be invoked once
+events.onexec(0x802CB1C0, function(e) {
+    console.log("func_802CB1C0 was called");
+    events.remove(e.callbackId);
+});
+
+ +

new AddressRange(start, end)
+
new AddressRange(start: number, end: number)
+

+Creates an immutable object with start and end address properties. + +

The following AddressRange objects are defined globally:

+ + + + + + + + + + + +
ADDR_ANY 0x00000000 : 0xFFFFFFFFAny address
ADDR_ANY_KUSEG 0x00000000 : 0x7FFFFFFFMIPS user mode TLB mapped segment
ADDR_ANY_KSEG0 0x80000000 : 0x9FFFFFFFMIPS cached unmapped segment
ADDR_ANY_KSEG1 0xA0000000 : 0xBFFFFFFFMIPS uncached unmapped segment
ADDR_ANY_KSEG2 0xC0000000 : 0xFFFFFFFFMIPS kernel mode TLB mapped segment
ADDR_ANY_RDRAM 0x80000000 : 0x807FFFFFCached RDRAM
ADDR_ANY_RDRAM_UNC 0xA0000000 : 0xA07FFFFFUncached RDRAM
ADDR_ANY_CART_ROM 0x90000000 : 0x95FFFFFFCached cartridge ROM
ADDR_ANY_CART_ROM_UNC 0xB0000000 : 0xB5FFFFFFUncached cartridge ROM
+ +
+ +
+
N64Image
+N64 image class + +

new N64Image(width, height[, format [, pixels[, palette]]])
+
new N64Image(width: number, height: number, format: number = IMG_RGBA32, pixels?: Buffer, palette?: Buffer)
+

+Creates an N64Image object. +

+width: Width of the image.
+height: Height of the image.
+format: Optional image format. IMG_RGBA32 by default. See the table below for supported formats.
+pixels: Optional Buffer object containing pixel data. +The byte length must be equal to (width * height * N64Image.bpp(format)) / 8. Copied to image.pixels.
+palette: Optional Buffer object containing palette data. Copied to image.palette. +

+ + + + + + + + + + + + + + + +
FormatPixelsPalette
IMG_RGBA16 16-bit colors (r5g5b5a1) (not used)
IMG_RGBA32 32-bit colors (r8g8b8a8) (not used)
IMG_CI4_RGBA164-bit color indices 16-bit colors (r5g5b5a1)
IMG_CI4_IA16 4-bit color indices 16-bit intensity/alpha values (i8a8)
IMG_CI8_RGBA168-bit color indices 16-bit colors (r5g5b5a1)
IMG_CI8_IA16 8-bit color indices 16-bit intensity/alpha values (i8a8)
IMG_IA4 4-bit intensity/alpha values (i3a1) (not used)
IMG_IA8 8-bit intensity/alpha values (i4a4) (not used)
IMG_IA16 16-bit intensity/alpha values (i8a8)(not used)
IMG_I4 4-bit intensity values (not used)
IMG_I8 8-bit intensity values (not used)
+ +

N64Image.fromPNG(pngData[, format])
+
N64Image.fromPNG(pngData: Buffer, format?: number = IMG_RGBA32): N64Image
+

+Static function. Creates an N64Image object from a PNG file. +
+var image = N64Image.fromPNG(fs.readfile("image.png"));
+
+Color quantization is not implemented. An error is thrown if format is a color-index (CI) type +and the color count of the PNG image exceeds the maximum number of colors supported. The maximum +number of colors supported by the CI4 and CI8 formats are 16 and 256, respectively. + +

N64Image.format(gbiFmt, gbiSiz[, gbiTlutFmt])
+
N64Image.format(gbiFmt: number, gbiSiz: number, gbiTlutFmt?: number): number
+

+

Static function. Returns a simplified image format number from a given GBI pixel size and format configuration. +Returns -1 if the configuration is invalid or unsupported. See the table below for supported configurations.

+ + + + + + + + + + + + + + + +
gbiFmt gbiSiz gbiTlutFmt Return value
G_IM_FMT_RGBA G_IM_SIZ_16b (not used) IMG_RGBA16
G_IM_FMT_RGBA G_IM_SIZ_32b (not used) IMG_RGBA32
G_IM_FMT_CI G_IM_SIZ_4b G_TT_RGBA16IMG_CI4_RGBA16
G_IM_FMT_CI G_IM_SIZ_4b G_TT_IA16 IMG_CI4_IA16
G_IM_FMT_CI G_IM_SIZ_8b G_TT_RGBA16IMG_CI8_RGBA16
G_IM_FMT_CI G_IM_SIZ_8b G_TT_IA16 IMG_CI8_IA16
G_IM_FMT_IA G_IM_SIZ_4b (not used) IMG_IA4
G_IM_FMT_IA G_IM_SIZ_8b (not used) IMG_IA8
G_IM_FMT_IA G_IM_SIZ_16b (not used) IMG_IA16
G_IM_FMT_I G_IM_SIZ_4b (not used) IMG_I4
G_IM_FMT_I G_IM_SIZ_8b (not used) IMG_I8
+ +

N64Image.bpp(format)
+
N64Image.bpp(format: number): number
+

+Static function. Returns the number of bits used per pixel for a given image format number or G_IM_SIZ_* constant. Does not include bits used for palette data. +
+N64Image.bpp(IMG_CI8_RGBA16); // 8
+N64Image.bpp(G_IM_SIZ_16b); // 16
+
+ +

image.toPNG()
+
image.toPNG(): Buffer
+

+Returns a PNG representation of the image data as a Buffer object. +
+fs.writefile("image.png", image.toPNG());
+
+ +

image.update()
+
image.update(): void
+

+Updates the internal bitmap. Should be called after alterations are made to image.pixels or image.palette. + +

image.format
+
image.format: number
+

+The format ID. Read-only. + +

image.width
+
image.width: number
+

+Width of the image. Read-only. + +

image.height
+
image.height: number
+

+Height of the image. Read-only. + +

image.pixels
+
image.pixels: Buffer
+

+The pixel data of the image. + +

image.palette
+
image.palette: Buffer
+

+The palette data of the image. null if the image does not use a color-index format. + +
+ +
+
cpu
+CPU register interface + +

cpu.pc
+

+Variable representing the CPU's program counter. + +

cpu.hi
+

+Variable representing the lower 32 bits of the CPU's HI register. + +

cpu.lo
+

+Variable representing the lower 32 bits of the CPU's LO register. + +

cpu.uhi
+

+Variable representing the upper 32 bits of the CPU's HI register. + +

cpu.ulo
+

+Variable representing the upper 32 bits of the CPU's LO register. + +

cpu.gpr.r0|at|v0|v1..ra
+
cpu.gpr[0..31]
cpu.gpr.r0|at|v0|v1|a0|a1|a2|a3|t0|t1|t2|t3|t4|t5|t6|t7|s0|s1|s2|s3|s4|s5|s6|s7|t8|t9|k0|k1|gp|sp|fp|ra
+

+Variables representing the lower 32 bits of the CPU's general purpose registers. + +

cpu.ugpr.r0|at|v0|v1..ra
+
cpu.ugpr[0..31]
cpu.ugpr.r0|at|v0|v1|a0|a1|a2|a3|t0|t1|t2|t3|t4|t5|t6|t7|s0|s1|s2|s3|s4|s5|s6|s7|t8|t9|k0|k1|gp|sp|fp|ra
+

+Variables representing the upper 32 bits of the CPU's general purpose registers. + +

cpu.fpr.f0..f31
+
cpu.fpr[0..31]

+Variables representing the CPU's 32-bit floating-point registers. + +

cpu.dfpr.f0..f30
+
cpu.dfpr[0..30]

+Variables representing the CPU's 64-bit floating-point registers. + +

cpu.cop0.index..errorepc
+

+

Variables representing the CPU's Coprocessor 0 registers.

+ + + + + + + + + + + + + + + + + + + +
cpu.cop0.index Index register.
cpu.cop0.random Random register.
cpu.cop0.entrylo0 EntryLo0 register.
cpu.cop0.entrylo1 EntryLo1 register.
cpu.cop0.context Context register.
cpu.cop0.pagemask PageMask register.
cpu.cop0.wired Wired register.
cpu.cop0.badvaddr BadVAddr register.
cpu.cop0.count Count register.
cpu.cop0.entryhi EntryHi register.
cpu.cop0.compare Compare register.
cpu.cop0.status Status register.
cpu.cop0.cause Cause register. Generates an interrupt when written.
cpu.cop0.epc EPC register.
cpu.cop0.config Config register.
cpu.cop0.taglo TagLo register.
cpu.cop0.taghi TagHi register.
cpu.cop0.errorepc ErrorEPC register.
+ +
+ +
+
debug
+Debugger interface + +

debug.breakhere([silent])
+
debug.breakhere(silent?: boolean = false): void
+

+Pauses emulation via the debugger. The CPU commands window is not shown if silent is true. +

+Emulation may be resumed by calling debug.resume() or clicking the "Go" button in the CPU commands window. +

+
+// Break if the CPU tries to write 5 to 0x80339EA8
+events.onwrite(0x80339EA8, function(e) {
+    if(e.value == 5) {
+        debug.breakhere();
+    }
+});
+
+ +

debug.resume()
+
debug.resume(): void
+

+Resumes emulation from a debugger pause. + +

debug.step()
+
debug.step(): void
+

+Executes a single CPU command. Does nothing if emulation is not paused by the debugger. + +

debug.skip()
+
debug.skip(): void
+

+Skips the current CPU command. Useful for preventing or overriding an operation. +
+// Do not let the CPU write to 0x8033B21E
+events.onwrite(0x8033B21E, function() {
+    debug.skip();
+});
+
+ +

debug.showmemory(address[, physical])
+
debug.showmemory(address: number, physical?: boolean = false): void
+

+Shows the memory window at address. Physical address space is used if physical is true. + +

debug.showcommands(address)
+
debug.showcommands(address: number): void
+

+Shows the CPU commands window at address. + +

debug.paused
+
debug.paused: boolean
+

+true if emulation is currently paused by the debugger. + +
+ +
+
asm
+ASM utilities + +

asm.encode(command[, address])
+
asm.encode(command: string, address?: number): number
+

+Encodes a single MIPS R4300i opcode. address is an optional parameter that may be used to hint the address of the instruction for branch offset calculation. +

Returns the opcode as a number.

+
+asm.encode("nop");                      // 0x00000000
+asm.encode("addiu sp, sp, -24");        // 0x27BDFFE8
+asm.encode("b 0x80400000", 0x803FFF00); // 0x1000003F
+asm.encode("b 0x80400000", 0x80400100); // 0x1000FFBF
+
+ +

asm.decode(opcode[, address])
+
asm.decode(opcode: number, address?: number)
+

+Decodes a single MIPS R4300i opcode. address is an optional parameter that may be used to hint the address of the instruction for branch address calculation. +

Returns a line of assembly code as a string.

+
+asm.decode(0x00000000);             // "NOP"
+asm.decode(0x27BDFFE8);             // "ADDIU SP, SP, -0x18"
+asm.decode(0x1000003F, 0x803FFF00); // "B 0x80400000"
+asm.decode(0x1000FFBF, 0x80400100); // "B 0x80400000"
+
+ +

asm.gprname(regIndex)
+
asm.gprname(regIndex: number): string
+

+Returns the name of the general purpose register specified by regIndex. +
+asm.gprname(4); // Returns "a0"
+
+ +
+ +
+
fs
+File system interface + +

fs.open(path, mode)
+
fs.open(path: string, mode: string): number
+

+Opens the file pointed to by path in the mode specified by mode. +See fopen for a list of valid modes. +

Returns a file descriptor.

+ +

fs.close(fd)
+
fs.close(fd): void
+

+Closes the file referenced by fd. + +

fs.write(fd, buffer[, offset[, length[, position]]])
+
fs.write(fd: number, buffer: string | Buffer | ArrayBuffer, offset?: number, length?: number, position?: number): number
+

+Writes buffer to the file referenced by file descriptor fd. buffer may be one of the following types: +ArrayBuffer, +Buffer, +DataView, +TypedArray, string. +

+offset: Optional position in the source buffer.
+length: Optional maximum number of bytes to write.
+position: Optional file position. +

+

Returns the number of bytes written.

+
+var fd = fs.open("file.txt", "wb");
+fs.write(fd, "Hello ");
+fs.write(fd, "world!\n");
+fs.close(fd);
+
+ +

fs.writefile(path, buffer)
+
fs.writefile(path: string, buffer: string | Buffer | ArrayBuffer): void
+

+Writes buffer to the file specified by path. +
+fs.writefile("ram_dump.bin", mem.getblock(K0BASE, mem.ramSize));
+
+ +

fs.read(fd, buffer, offset, length, position)
+
fs.read(fd: number, buffer: Buffer | ArrayBuffer, offset: number, length: number, position: number) : number
+

+Reads data from the file referenced by fd into buffer. buffer may be one of the following types: +ArrayBuffer, +Buffer, +DataView, +TypedArray.

+offset: Position in the source buffer.
+length: Number of bytes to read.
+position: File position. +

Returns the number of bytes read.

+ +

fs.readfile(path)
+
fs.readfile(path: string): Buffer
+

+Returns a Buffer object representing the data of the file specified by path. + +

fs.fstat(fd)
+
fs.fstat(fd: number): fs.Stats
+

+Returns an fs.Stats object containing information about the file referenced by fd. + +

fs.stat(path)
+
fs.stat(path: string): fs.Stats
+

+Returns an fs.Stats object containing information about the file specified by path. + +

fs.unlink(path)
+
fs.unlink(path: string): boolean
+

+Deletes the file specified by path. Returns true if the operation is successful. + +

fs.mkdir(path)
+
fs.mkdir(path: string): boolean
+

+Creates a directory. Returns true if the operation is successful. + +

fs.rmdir(path)
+
fs.rmdir(path: string): boolean
+

+Deletes a directory. The directory must be empty. Returns true if the operation is successful. + +

fs.readdir(path)
+
fs.readdir(path: string): string[]
+

+Returns an array of file names from a directory. + +

fs.Stats
+

+

Object containing information about a file or directory. Generated by fs.fstat/fs.stat.

+

+ + + + + + + + + + + + + + + +
stats.devID of the device the file resides on
stats.inoinode number
stats.modeFile permissions
stats.nlinkNumber of links to the file
stats.uidUser ID
stats.gidGroup ID
stats.rdevDevice ID (if file is character or block special)
stats.sizeSize of the file in bytes
stats.atimeMsLast access timestamp in milliseconds
stats.mtimeMsLast modification timestamp in milliseconds
stats.ctimeMsCreation timestamp in milliseconds
stats.atimeJS Date object representing the last access time
stats.mtimeJS Date object representing the last modification time
stats.ctimeJS Date object representing the creation time
+

+ +stats.isDirectory()
+
stats.isDirectory(): boolean
+

Returns true if the fs.Stats object describes a directory.

+ +stats.isFile()
+
stats.isFile(): boolean
+

Returns true if the fs.Stats object describes a regular file.

+ +
+ +
+
Socket
+TCP socket class + +

new Socket([options])
+
new Socket(options?: Object)
+

+

Creates a Socket object. options may contain the following properties:

+ + +
allowHalfOpenIf true, the socket will not automatically fully close when the read half is closed. false by default.
+ +

socket.connect(port, host[, connectListener])
+
socket.connect(port: number, host: string, connectListener: () => void): void
+

+Initiates a TCP connection. +Optional connectListener is invoked when the connection is established (i.e the socket is writable). +
+var client = new Socket();
+
+client.connect(80, 'www.example.com', function() {
+    var response = '';
+
+    client.on('data', function(data) {
+        response += data.toString();
+    });
+
+    client.on('end', function() {
+        console.log(response);
+    });
+
+    const message = [
+        "GET / HTTP/1.1",
+        "Host: www.example.com",
+        "Connection: close",
+        "\r\n"
+    ].join("\r\n");
+
+    client.end(message);
+});
+
+ +

socket.write(data[, callback])
+
socket.write(data: Buffer | string, callback?: () => void): void
+

+

Sends data on the socket.

+Optional callback is invoked when all bytes of data are sent. + +

socket.end([data[, callback]])
+
socket.end(data?: Buffer | string, callback?: () => void): void
+

+

Sends optional data and closes the write half of the socket after all buffered writes are sent.

+Optional callback is invoked when all buffered writes are sent and the write half of the socket is closed. + +

socket.close()
+
socket.close(): void
+

+Fully closes the socket. + +

socket.on('connect', listener)
+
socket.on('connect', listener: () => void): Socket
+

+

Registers a listener for the 'connect' event.

+listener is invoked when the connection is established. +

Returns the Socket object.

+ +

socket.on('lookup', listener)
+
socket.on('lookup', listener: (Object) => void): Socket
+

+

Registers a listener for the 'lookup' event.

+

listener receives an object with the following properties when the host name is resolved:

+ + + + + +
errnull, or an Error object if an error occurred (TODO)
addressIP address string
portPort number
family"IPv4" or "IPv6"
+

Returns the Socket object.

+ +

socket.on('data', listener)
+
socket.on('data', listener: (data: Buffer) => void): Socket
+

+

Registers a listener for the 'data' event.

+listener receives data as a Buffer object. +

Returns the Socket object.

+ +

socket.on('end', listener)
+
socket.on('end', listener: () => void): Socket
+

+

Registers a listener for the 'end' event.

+listener is invoked when the other side closes the read half of the socket. +

Returns the Socket object.

+ +

socket.on('close', listener)
+
socket.on('close', listener: () => void): Socket
+

+

Registers a listener for the 'close' event.

+listener is invoked when the socket is fully closed. +

Returns the Socket object.

+ +

socket.on('drain', listener)
+
socket.on('drain', listener: () => void): Socket
+

+

Registers a listener for the 'drain' event.

+listener is invoked when all buffered writes are sent. +

Returns the Socket object.

+ +

socket.on('error', listener)
+
socket.on('error', listener: (Error) => void): Socket
+

+

Registers a listener for the 'error' event.

+listener receives an Error object when an error occurs. +

Returns the Socket object.

+ +

socket.off(eventName, listener)
+
socket.off(eventName: string, listener: function): Socket
+

+Removes a listener. +

Returns the Socket object.

+ +

socket.remoteAddress
+
socket.remoteAddress: string
+

+The remote address. "" if the socket is not connected. + +

socket.remotePort
+
socket.remotePort: number
+

+The remote port. 0 if the socket is not connected. + +

socket.localAddress
+
socket.localAddress: string
+

+The local address. "" if the socket is not connected. + +

socket.localPort
+
socket.localPort: number
+

+The local port. "" if the socket is not connected. + +

socket.addressFamily
+
socket.addressFamily: string
+

+The address family. "" if the socket is not connected. + +
+ +
+
Server
+TCP server class + +

new Server()
+
new Server()
+

+Creates a Server object. +
+var server = new Server();
+server.listen(1337, "127.0.0.1");
+
+server.on('connection', function(c) {
+    c.on('data', function(data) {
+        if(data.toString() == 'ping') {
+            c.write('pong');
+        }
+    });
+
+    c.on('end', function() {
+        console.log("client ended connection");
+    })
+});
+
+ +

server.listen(port[, address])
+
server.listen(port: number, address: string): void
+

+Binds the server to the specified address and port and starts listening. + +

server.close()
+
server.close(): void
+

+Stops listening for new connections (does not affect existing connections). + +

server.on('connection', listener)
+
server.on('connection', listener: (Socket) => void): Server
+

+

Registers a listener for the 'connection' event.

+listener receives a Socket object when a client has connected. +

Returns the Server object.

+ +

server.on('listening', listener)
+
server.on('listening', listener: () => void): Server
+

+

Registers a listener for the 'listening' event.

+listener is invoked when the server begins listening for connections. +

Returns the Server object.

+ +

server.on('close', listener)
+
server.on('close', listener: () => void): Server
+

+

Registers a listener for the 'close' event.

+listener is invoked when the server is closed. +

Returns the Server object.

+ +

server.on('error', listener)
+
server.on('error', listener: (Error) => void): Server
+

+

Registers a listener for the 'error' event.

+listener receives an Error object when an error occurs. +

Returns the Server object.

+ +

server.off(eventName, listener)
+
server.off(eventName: string, listener: function): Server
+

+Removes a listener. +

Returns the Server object.

+ +

server.port
+
server.port: number
+

+The bound port number. 0 if the server is not listening. + +

server.address
+
server.address: string
+

+The bound address. "" if the server is not listening. + +

server.addressFamily
+
server.addressFamily: string
+

+The bound address family. "" if the server is not listening. + +
+ +
+
script
+Script instance configuration interface + +

script.keepalive(keepAlive)
+
script.keepalive(keepAlive: boolean): void
+

+Prevents the script instance from being destroyed when it has nothing left to do. +Useful when the default console input behavior is desired. + +

script.timeout(milliseconds)
+
script.timeout(milliseconds: number): void
+

+Sets the maximum amount of time tasks may run on the script instance before errors are thrown. By default this value is 500 milliseconds. +Setting this value to zero will allow tasks to run indefinitely. + +
+ +
+
Globals
+ +

global
+
global: Object
+

+Reference to the global object. +
+global.test = 1;
+console.log(test); // "1"
+
+ +

require(id)
+
require(id: string): Object
+

+Loads a module and returns its module.exports object. id may be one of the following: +

+

+

+
+/* Scripts/module_example.js */
+
+const mymodule = require('mymodule/lib.js'); // Loads Scripts/modules/mymodule/lib.js
+mymodule.init(); // Prints "Loaded mymodule"
+
+
+/* Scripts/modules/mymodule/lib.js */
+
+function init() {
+    console.log("Loaded mymodule");
+}
+
+module.exports = {
+    init: init
+};
+
+

Native modules use the Duktape C module convention.

+ +

exec(command[, options])
+
exec(command: string, options?: Object): string
+

+Executes a system command. Does not return until the subprocess exits. +

Returns the standard output of the subprocess as a string.

+ +

options may contain any of the following properties:

+ + + + + +
showWindowIf true, the window of the subprocess is visible. false by default.
verboseIf true, the output of the subprocess is sent to the script manager console. false by default.
cwdCurrent working directory of the subprocess. By default this is the directory containing Project64.
+ +

This function throws an error if the operation fails or if the exit code of the subprocess is non-zero. +The thrown error object contains the following properties:

+ + + + + + +
statusThe exit code of the subprocess.
stdoutThe standard output of the subprocess.
stderrThe standard error output of the subprocess.
pidThe PID of the subprocess.
+
+try {
+    exec("dir", { verbose: true });
+} catch(err) {
+    console.log("exec() failed with code", err.status);
+    console.log(err.stderr);
+}
+
+ +

alert(message[, caption])
+
alert(message: string, caption?: string): void
+

+Shows a message box over the main window. Does not return until the message box is dismissed. + +

setTimeout(callback, delay)
+
setTimeout(callback: () => void, delay: number): number
+

+callback is invoked once after the number of milliseconds specified by delay. +

Returns a timeout ID.

+ +

clearTimeout(timeoutId)
+
setTimeout(timeoutId: number): void
+

+Cancels the timeout specified by timeoutId. + +

setInterval(callback, delay)
+
setInterval(callback: () => void, delay: number): number
+

+callback is invoked periodically. delay specifies the amount of time in milliseconds to wait before each invocation. +

Returns an interval ID.

+ +

clearInterval(intervalId)
+
setTimeout(intervalId: number): void
+

+Cancels the interval specified by intervalId. + +

number.hex([numChars])
+
number.hex(numChars?: number): string
+

+Returns a hexadecimal string representation of the number object. +The returned string is prepended with zeroes so that its character length is numChars or 8 by default. +
+var n = 123;
+n.hex();  // 0000007B
+n.hex(4); // 007B
+
+ +

Type IDs
+

+ + + + + + + + + + + +
u8 Unsigned 8-bit integer
u16 Unsigned 16-bit integer
u32 Unsigned 32-bit integer
s8 Signed 8-bit integer
s16 Signed 16-bit integer
s32 Signed 32-bit integer
f32 32-bit single precision floating-point
f64 64-bit double precision floating-point
u64 Unsigned 64-bit integer
s64 Signed 64-bit integer
+ +

Note: The mem.bind*/mem.typedef APIs do not currently support u64 and s64.

+ +

N64 memory addresses
+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RDRAM_CONFIG_REG 0xA3F00000
RDRAM_DEVICE_TYPE_REG 0xA3F00000
RDRAM_DEVICE_ID_REG 0xA3F00004
RDRAM_DELAY_REG 0xA3F00008
RDRAM_MODE_REG 0xA3F0000C
RDRAM_REF_INTERVAL_REG 0xA3F00010
RDRAM_REF_ROW_REG 0xA3F00014
RDRAM_RAS_INTERVAL_REG 0xA3F00018
RDRAM_MIN_INTERVAL_REG 0xA3F0001C
RDRAM_ADDR_SELECT_REG 0xA3F00020
RDRAM_DEVICE_MANUF_REG 0xA3F00024
SP_MEM_ADDR_REG 0xA4040000
SP_DRAM_ADDR_REG 0xA4040004
SP_RD_LEN_REG 0xA4040008
SP_WR_LEN_REG 0xA404000C
SP_STATUS_REG 0xA4040010
SP_DMA_FULL_REG 0xA4040014
SP_DMA_BUSY_REG 0xA4040018
SP_SEMAPHORE_REG 0xA404001C
SP_PC_REG 0xA4080000
SP_IBIST_REG 0xA4080004
DPC_START_REG 0xA4100000
DPC_END_REG 0xA4100004
DPC_CURRENT_REG 0xA4100008
DPC_STATUS_REG 0xA410000C
DPC_CLOCK_REG 0xA4100010
DPC_BUFBUSY_REG 0xA4100014
DPC_PIPEBUSY_REG 0xA4100018
DPC_TMEM_REG 0xA410001C
DPS_TBIST_REG 0xA4200000
DPS_TEST_MODE_REG 0xA4200004
DPS_BUFTEST_ADDR_REG 0xA4200008
DPS_BUFTEST_DATA_REG 0xA420000C
MI_INIT_MODE_REG 0xA4300000
MI_MODE_REG 0xA4300000
MI_VERSION_REG 0xA4300004
MI_NOOP_REG 0xA4300004
MI_INTR_REG 0xA4300008
MI_INTR_MASK_REG 0xA430000C
VI_STATUS_REG 0xA4400000
VI_CONTROL_REG 0xA4400000
VI_ORIGIN_REG 0xA4400004
VI_DRAM_ADDR_REG 0xA4400004
VI_WIDTH_REG 0xA4400008
VI_H_WIDTH_REG 0xA4400008
VI_INTR_REG 0xA440000C
VI_V_INTR_REG 0xA440000C
VI_CURRENT_REG 0xA4400010
VI_V_CURRENT_LINE_REG 0xA4400010
VI_BURST_REG 0xA4400014
VI_TIMING_REG 0xA4400014
VI_V_SYNC_REG 0xA4400018
VI_H_SYNC_REG 0xA440001C
VI_LEAP_REG 0xA4400020
VI_H_SYNC_LEAP_REG 0xA4400020
VI_H_START_REG 0xA4400024
VI_H_VIDEO_REG 0xA4400024
VI_V_START_REG 0xA4400028
VI_V_VIDEO_REG 0xA4400028
VI_V_BURST_REG 0xA440002C
VI_X_SCALE_REG 0xA4400030
VI_Y_SCALE_REG 0xA4400034
AI_DRAM_ADDR_REG 0xA4500000
AI_LEN_REG 0xA4500004
AI_CONTROL_REG 0xA4500008
AI_STATUS_REG 0xA450000C
AI_DACRATE_REG 0xA4500010
AI_BITRATE_REG 0xA4500014
PI_DRAM_ADDR_REG 0xA4600000
PI_CART_ADDR_REG 0xA4600004
PI_RD_LEN_REG 0xA4600008
PI_WR_LEN_REG 0xA460000C
PI_STATUS_REG 0xA4600010
PI_BSD_DOM1_LAT_REG 0xA4600014
PI_BSD_DOM1_PWD_REG 0xA4600018
PI_BSD_DOM1_PGS_REG 0xA460001C
PI_BSD_DOM1_RLS_REG 0xA4600020
PI_BSD_DOM2_LAT_REG 0xA4600024
PI_BSD_DOM2_PWD_REG 0xA4600028
PI_BSD_DOM2_PGS_REG 0xA460002C
PI_BSD_DOM2_RLS_REG 0xA4600030
RI_MODE_REG 0xA4700000
RI_CONFIG_REG 0xA4700004
RI_CURRENT_LOAD_REG 0xA4700008
RI_SELECT_REG 0xA470000C
RI_REFRESH_REG 0xA4700010
RI_COUNT_REG 0xA4700010
RI_LATENCY_REG 0xA4700014
RI_RERROR_REG 0xA4700018
RI_WERROR_REG 0xA470001C
SI_DRAM_ADDR_REG 0xA4800000
SI_PIF_ADDR_RD64B_REG 0xA4800004
SI_PIF_ADDR_WR64B_REG 0xA4800010
SI_STATUS_REG 0xA4800018
+

+

+ + + + + +
PIF_ROM_START 0xBFC00000
PIF_RAM_START 0xBFC007C0
SP_DMEM_START 0xA4000000
SP_IMEM_START 0xA4001000
+

+

+ + + + + +
KUBASE 0x00000000
K0BASE 0x80000000
K1BASE 0xA0000000
K2BASE 0xC0000000
+

+

+ + + + + + +
UT_VEC 0x80000000
R_VEC 0xBFC00000
XUT_VEC 0x80000080
ECC_VEC 0x80000100
E_VEC 0x80000180
+

+ +

Miscellaneous
+

+

+ + + +
M_GFXTASK1
M_AUDTASK2
+

+

+ + + +
OS_READ0
OS_WRITE1
+

+

+ + + + + + +
G_IM_FMT_RGBA0
G_IM_FMT_YUV1
G_IM_FMT_CI2
G_IM_FMT_IA3
G_IM_FMT_I4
+

+

+ + + + + +
G_IM_SIZ_4b0
G_IM_SIZ_8b1
G_IM_SIZ_16b2
G_IM_SIZ_32b3
+

+

+ + + + +
G_TT_NONE 0x0000
G_TT_RGBA160x8000
G_TT_IA16 0xC000
+

+ +
+
+ diff --git a/Scripts/api_documentation.js b/Scripts/api_documentation.js new file mode 100644 index 000000000..273abaaa4 --- /dev/null +++ b/Scripts/api_documentation.js @@ -0,0 +1,3 @@ +const docpath = pj64.installDirectory + "JS-API-Documentation.html"; +console.log("Opening " + docpath + " ..."); +exec("start " + docpath); diff --git a/Scripts/example.js b/Scripts/example.js index d0fbf715e..69986c884 100644 --- a/Scripts/example.js +++ b/Scripts/example.js @@ -1 +1,2 @@ -console.log("Hello world! See apidoc.htm for documentation."); +console.log("Hello world! See \"JS-API-Documentation.html\" for API documentation."); +script.keepalive(true); diff --git a/Source/3rdParty/duktape/AUTHORS.rst b/Source/3rdParty/duktape/AUTHORS.rst index 2799f70f4..70ce87c41 100644 --- a/Source/3rdParty/duktape/AUTHORS.rst +++ b/Source/3rdParty/duktape/AUTHORS.rst @@ -29,6 +29,45 @@ and agreed to irrevocably license their contributions under the Duktape * René Hollander * Julien Hamaide (https://github.com/crazyjul) * Sebastian Götte (https://github.com/jaseg) +* Tomasz Magulski (https://github.com/magul) +* \D. Bohdan (https://github.com/dbohdan) +* Ondřej Jirman (https://github.com/megous) +* Saúl Ibarra Corretgé +* Jeremy HU +* Ole André Vadla Ravnås (https://github.com/oleavr) +* Harold Brenes (https://github.com/harold-b) +* Oliver Crow (https://github.com/ocrow) +* Jakub Chłapiński (https://github.com/jchlapinski) +* Brett Vickers (https://github.com/beevik) +* Dominik Okwieka (https://github.com/okitec) +* Remko Tronçon (https://el-tramo.be) +* Romero Malaquias (rbsm@ic.ufal.br) +* Michael Drake +* Steven Don (https://github.com/shdon) +* Simon Stone (https://github.com/sstone1) +* \J. McC. (https://github.com/jmhmccr) +* Jakub Nowakowski (https://github.com/jimvonmoon) +* Tommy Nguyen (https://github.com/tn0502) +* Fabrice Fontaine (https://github.com/ffontaine) +* Christopher Hiller (https://github.com/boneskull) +* Gonzalo Diethelm (https://github.com/gonzus) +* Michal Kasperek (https://github.com/michalkas) +* Andrew Janke (https://github.com/apjanke) +* Steve Fan (https://github.com/stevefan1999) +* Edward Betts (https://github.com/edwardbetts) +* Ozhan Duz (https://github.com/webfolderio) +* Akos Kiss (https://github.com/akosthekiss) +* TheBrokenRail (https://github.com/TheBrokenRail) +* Jesse Doyle (https://github.com/jessedoyle) +* Gero Kuehn (https://github.com/dc6jgk) +* James Swift (https://github.com/phraemer) +* Luis de Bethencourt (https://github.com/luisbg) +* Ian Whyman (https://github.com/v00d00) +* Rick Sayre (https://github.com/whorfin) +* Craig Leres (https://github.com/leres) +* Maurici Abad (https://github.com/mauriciabad) +* Nancy Li (https://github.com/NancyLi1013) +* William Parks (https://github.com/WilliamParks) Other contributions =================== @@ -66,7 +105,11 @@ bugs, provided ideas, etc; roughly in order of appearance): * Michael Drake (https://github.com/tlsa) * https://github.com/chris-y * Laurent Zubiaur (https://github.com/lzubiaur) -* Ole André Vadla Ravnås (https://github.com/oleavr) +* Neil Kolban (https://github.com/nkolban) +* Wilhelm Wanecek (https://github.com/wanecek) +* Andrew Janke (https://github.com/apjanke) +* Unamer (https://github.com/unamer) +* Karl Dahlke (eklhad@gmail.com) If you are accidentally missing from this list, send me an e-mail -(``sami.vaarala@iki.fi``) and I'll fix the omission. +(``sami.vaarala@iki.fi``) and I'll fix the omission. \ No newline at end of file diff --git a/Source/3rdParty/duktape/LICENSE.txt b/Source/3rdParty/duktape/LICENSE.txt index 1b1c382c3..0027e40a9 100644 --- a/Source/3rdParty/duktape/LICENSE.txt +++ b/Source/3rdParty/duktape/LICENSE.txt @@ -1,10 +1,6 @@ -=============== -Duktape license -=============== +The MIT License (MIT) -(http://opensource.org/licenses/MIT) - -Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst) +Copyright (c) 2013-present, Duktape authors (see AUTHORS.rst) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -22,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +THE SOFTWARE. \ No newline at end of file diff --git a/Source/3rdParty/duktape/README.rst b/Source/3rdParty/duktape/README.rst deleted file mode 100644 index 5c96275ef..000000000 --- a/Source/3rdParty/duktape/README.rst +++ /dev/null @@ -1,110 +0,0 @@ -======= -Duktape -======= - -Duktape is a small and portable Ecmascript E5/E5.1 implementation. It is -intended to be easily embeddable into C programs, with a C API similar in -spirit to Lua's. - -Duktape supports the full E5/E5.1 feature set including errors, Unicode -strings, and regular expressions, a subset of E6 features (e.g. Proxy -objects), Khronos/ES6 ArrayBuffer/TypedView, and Node.js Buffer bindings. - -Duktape also provides a number of custom features such as error tracebacks, -additional data types for better C integration, combined reference counting -and mark-and sweep garbage collector, object finalizers, co-operative -threads a.k.a. coroutines, tail calls, built-in logging and module frameworks, -a built-in debugger protocol, function bytecode dump/load, and so on. - -You can browse Duktape programmer's API and other documentation at: - -* http://duktape.org/ - -In particular, you should read the getting started section: - -* http://duktape.org/guide.html#gettingstarted - -More examples and how-to articles are in the Duktape Wiki: - -* http://wiki.duktape.org/ - -Building and integrating Duktape into your project is very straightforward: - -* http://duktape.org/guide.html#compiling - -See Makefile.hello for a concrete example:: - - $ cd - $ make -f Makefile.hello - [...] - $ ./hello - Hello world! - 2+3=5 - -To build an example command line tool, use the following:: - - $ cd - $ make -f Makefile.cmdline - [...] - - $ ./duk - ((o) Duktape - duk> print('Hello world!'); - Hello world! - = undefined - - $ ./duk mandel.js - [...] - -This distributable contains: - -* ``src/``: main Duktape library in a "single source file" format (duktape.c, - duktape.h, and duk_config.h). - -* ``src-noline/``: contains a variant of ``src/duktape.c`` with no ``#line`` - directives which is preferable for some users. See discussion in - https://github.com/svaarala/duktape/pull/363. - -* ``src-separate/``: main Duktape library in multiple files format. - -* ``config/``: genconfig utility for creating duk_config.h configuration - files, see: http://wiki.duktape.org/Configuring.html. - -* ``examples/``: further examples for using Duktape. Although Duktape - itself is widely portable, some of the examples are Linux only. - For instance the ``eventloop`` example illustrates how ``setTimeout()`` - and other standard timer functions could be implemented on Unix/Linux. - -* ``extras/``: utilities and modules which don't comfortably fit into the - main Duktape library because of footprint or portability concerns. - Extras are maintained and bug fixed code, but don't have the same version - guarantees as the main Duktape library. - -* ``polyfills/``: a few replacement suggestions for non-standard Javascript - functions provided by other implementations. - -* ``debugger/``: a debugger with a web UI, see ``debugger/README.rst`` and - https://github.com/svaarala/duktape/blob/master/doc/debugger.rst for - details on Duktape debugger support. Also contains a JSON debug proxy - (one written in Node.js and another in DukLuv) to make talking to the - debug target easier. - -* ``licenses/``: licensing information. - -You can find release notes at: - -* https://github.com/svaarala/duktape/blob/master/RELEASES.rst - -This distributable contains Duktape version 1.5.1, created from git -commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1). - -Duktape is copyrighted by its authors (see ``AUTHORS.rst``) and licensed -under the MIT license (see ``LICENSE.txt``). String hashing algorithms are -based on the algorithm from Lua (MIT license), djb2 hash, and Murmurhash2 -(MIT license). Duktape module loader is based on the CommonJS module -loading specification (without sharing any code), CommonJS is under the -MIT license. - -Have fun! - -Sami Vaarala (sami.vaarala@iki.fi) diff --git a/Source/3rdParty/duktape/duk_config.h b/Source/3rdParty/duktape/duk_config.h index c336603eb..a84d8fb2f 100644 --- a/Source/3rdParty/duktape/duk_config.h +++ b/Source/3rdParty/duktape/duk_config.h @@ -1,23 +1,28 @@ /* * duk_config.h configuration header generated by genconfig.py. * - * Git commit: 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e - * Git describe: v1.5.1 - * Git branch: HEAD + * Git commit: fffa346eff06a8764b02c31d4336f63a773a95c3 + * Git describe: v2.6.0 + * Git branch: v2-maintenance * * Supported platforms: * - Mac OSX, iPhone, Darwin + * - Orbis * - OpenBSD * - Generic BSD * - Atari ST TOS * - AmigaOS + * - Durango (XboxOne) * - Windows * - Flashplayer (Crossbridge) * - QNX * - TI-Nspire * - Emscripten + * - Android * - Linux * - Solaris + * - AIX + * - HPUX * - Generic POSIX * - Cygwin * - Generic UNIX @@ -35,6 +40,8 @@ * - PowerPC 64-bit * - SPARC 32-bit * - SPARC 64-bit + * - RISC-V 32-bit + * - RISC-V 64-bit * - SuperH * - Motorola 68k * - Emscripten @@ -60,20 +67,24 @@ */ /* DLL build detection */ -#if defined(DUK_OPT_DLL_BUILD) -#define DUK_F_DLL_BUILD -#elif defined(DUK_OPT_NO_DLL_BUILD) -#undef DUK_F_DLL_BUILD -#else /* not configured for DLL build */ #undef DUK_F_DLL_BUILD -#endif /* Apple OSX, iOS */ #if defined(__APPLE__) #define DUK_F_APPLE #endif +/* FreeBSD */ +#if defined(__FreeBSD__) || defined(__FreeBSD) +#define DUK_F_FREEBSD +#endif + +/* Orbis (PS4) variant */ +#if defined(DUK_F_FREEBSD) && defined(__ORBIS__) +#define DUK_F_ORBIS +#endif + /* OpenBSD */ #if defined(__OpenBSD__) || defined(__OpenBSD) #define DUK_F_OPENBSD @@ -84,11 +95,6 @@ #define DUK_F_NETBSD #endif -/* FreeBSD */ -#if defined(__FreeBSD__) || defined(__FreeBSD) -#define DUK_F_FREEBSD -#endif - /* BSD variant */ #if defined(DUK_F_FREEBSD) || defined(DUK_F_NETBSD) || defined(DUK_F_OPENBSD) || \ defined(__bsdi__) || defined(__DragonFly__) @@ -126,6 +132,11 @@ #endif #endif +/* Durango (Xbox One) */ +#if defined(_DURANGO) || defined(_XBOX_ONE) +#define DUK_F_DURANGO +#endif + /* Windows, both 32-bit and 64-bit */ #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || \ defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) @@ -162,6 +173,10 @@ #define DUK_F_BCC #endif +#if defined(ANDROID) || defined(__ANDROID__) +#define DUK_F_ANDROID +#endif + /* Linux */ #if defined(__linux) || defined(__linux__) || defined(linux) #define DUK_F_LINUX @@ -170,6 +185,28 @@ /* illumos / Solaris */ #if defined(__sun) && defined(__SVR4) #define DUK_F_SUN +#if defined(__SUNPRO_C) && (__SUNPRO_C < 0x550) +#define DUK_F_OLD_SOLARIS +/* Defines _ILP32 / _LP64 required by DUK_F_X86/DUK_F_X64. Platforms + * are processed before architectures, so this happens before the + * DUK_F_X86/DUK_F_X64 detection is emitted. + */ +#include +#endif +#endif + +/* AIX */ +#if defined(_AIX) +/* defined(__xlc__) || defined(__IBMC__): works but too wide */ +#define DUK_F_AIX +#endif + +/* HPUX */ +#if defined(__hpux) +#define DUK_F_HPUX +#if defined(__ia64) +#define DUK_F_HPUX_ITANIUM +#endif #endif /* POSIX */ @@ -188,26 +225,12 @@ #define DUK_F_UNIX #endif -/* stdint.h not available */ -#if defined(DUK_F_WINDOWS) && defined(_MSC_VER) -#if (_MSC_VER < 1700) -/* VS2012+ has stdint.h, < VS2012 does not (but it's available for download). */ -#define DUK_F_NO_STDINT_H -#endif -#endif -#if !defined(DUK_F_NO_STDINT_H) && (defined(DUK_F_TOS) || defined(DUK_F_BCC)) -#define DUK_F_NO_STDINT_H -#endif - -/* C++ */ -#undef DUK_F_CPP -#if defined(__cplusplus) -#define DUK_F_CPP -#endif - /* Intel x86 (32-bit), x64 (64-bit) or x32 (64-bit but 32-bit pointers), * define only one of DUK_F_X86, DUK_F_X64, DUK_F_X32. * https://sites.google.com/site/x32abi/ + * + * With DUK_F_OLD_SOLARIS the header must be included + * before this. */ #if defined(__amd64__) || defined(__amd64) || \ defined(__x86_64__) || defined(__x86_64) || \ @@ -230,9 +253,9 @@ #endif /* ARM */ -#if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) +#if defined(__arm__) || defined(__thumb__) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARM64) || defined(__aarch64__) #define DUK_F_ARM -#if defined(__LP64__) || defined(_LP64) || defined(__arm64) || defined(__arm64__) +#if defined(__LP64__) || defined(_LP64) || defined(__arm64) || defined(__arm64__) || defined(_M_ARM64) || defined(__aarch64__) #define DUK_F_ARM64 #else #define DUK_F_ARM32 @@ -264,6 +287,22 @@ #endif #endif +/* RISC-V, https://github.com/riscv/riscv-toolchain-conventions#cc-preprocessor-definitions */ +#if defined(__riscv) +#define DUK_F_RISCV +#if defined(__riscv_xlen) +#if (__riscv_xlen == 32) +#define DUK_F_RISCV32 +#elif (__riscv_xlen == 64) +#define DUK_F_RISCV64 +#else +#error __riscv_xlen has unsupported value (not 32 or 64) +#endif +#else +#error __riscv defined without __riscv_xlen +#endif +#endif /* __riscv */ + /* SuperH */ #if defined(__sh__) || \ defined(__sh1__) || defined(__SH1__) || \ @@ -279,6 +318,12 @@ #define DUK_F_CLANG #endif +/* C++ */ +#undef DUK_F_CPP +#if defined(__cplusplus) +#define DUK_F_CPP +#endif + /* C99 or above */ #undef DUK_F_C99 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) @@ -380,6 +425,20 @@ #define DUK_JMPBUF_TYPE jmp_buf #define DUK_SETJMP(jb) _setjmp((jb)) #define DUK_LONGJMP(jb) _longjmp((jb), 1) +#elif defined(DUK_F_ORBIS) +/* --- Orbis --- */ +/* Orbis = PS4 */ +#define DUK_USE_DATE_NOW_GETTIMEOFDAY +#define DUK_USE_DATE_TZO_GMTIME_S +/* no parsing (not an error) */ +#define DUK_USE_DATE_FMT_STRFTIME +#include +#include +#include +#include +#include + +#define DUK_USE_OS_STRING "orbis" #elif defined(DUK_F_OPENBSD) /* --- OpenBSD --- */ /* http://www.monkey.org/openbsd/archive/ports/0401/msg00089.html */ @@ -436,7 +495,7 @@ #define DUK_USE_DATE_PRS_STRPTIME #define DUK_USE_DATE_FMT_STRFTIME #include -#ifndef UINTPTR_MAX +#if !defined(UINTPTR_MAX) #define UINTPTR_MAX UINT_MAX #endif #else @@ -449,8 +508,47 @@ #if !defined(DUK_USE_BYTEORDER) && (defined(DUK_F_M68K) || defined(DUK_F_PPC)) #define DUK_USE_BYTEORDER 3 #endif +#elif defined(DUK_F_DURANGO) +/* --- Durango (XboxOne) --- */ +/* Durango = XboxOne + * Configuration is nearly identical to Windows, except for + * DUK_USE_DATE_TZO_WINDOWS. + */ + +/* Initial fix: disable secure CRT related warnings when compiling Duktape + * itself (must be defined before including Windows headers). Don't define + * for user code including duktape.h. + */ +#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +/* MSVC does not have sys/param.h */ +#define DUK_USE_DATE_NOW_WINDOWS +#define DUK_USE_DATE_TZO_WINDOWS_NO_DST +/* Note: PRS and FMT are intentionally left undefined for now. This means + * there is no platform specific date parsing/formatting but there is still + * the ISO 8601 standard format. + */ +#if defined(DUK_COMPILING_DUKTAPE) +/* Only include when compiling Duktape to avoid polluting application build + * with a lot of unnecessary defines. + */ +#include +#endif + +#define DUK_USE_OS_STRING "durango" + +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 1 +#endif #elif defined(DUK_F_WINDOWS) /* --- Windows --- */ +/* Windows version can't obviously be determined at compile time, + * but _WIN32_WINNT indicates the minimum version targeted: + * - https://msdn.microsoft.com/en-us/library/6sehtctf.aspx + */ + /* Initial fix: disable secure CRT related warnings when compiling Duktape * itself (must be defined before including Windows headers). Don't define * for user code including duktape.h. @@ -461,12 +559,7 @@ /* Windows 32-bit and 64-bit are currently the same. */ /* MSVC does not have sys/param.h */ -#define DUK_USE_DATE_NOW_WINDOWS -#define DUK_USE_DATE_TZO_WINDOWS -/* Note: PRS and FMT are intentionally left undefined for now. This means - * there is no platform specific date parsing/formatting but there is still - * the ISO 8601 standard format. - */ + #if defined(DUK_COMPILING_DUKTAPE) /* Only include when compiling Duktape to avoid polluting application build * with a lot of unnecessary defines. @@ -474,6 +567,34 @@ #include #endif +/* GetSystemTimePreciseAsFileTime() available from Windows 8: + * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx + */ +#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS) +/* User forced provider. */ +#else +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602) +#define DUK_USE_DATE_NOW_WINDOWS_SUBMS +#else +#define DUK_USE_DATE_NOW_WINDOWS +#endif +#endif + +#define DUK_USE_DATE_TZO_WINDOWS + +/* Note: PRS and FMT are intentionally left undefined for now. This means + * there is no platform specific date parsing/formatting but there is still + * the ISO 8601 standard format. + */ + +/* QueryPerformanceCounter() may go backwards in Windows XP, so enable for + * Vista and later: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions + */ +#if !defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) && \ + defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) +#define DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC +#endif + #define DUK_USE_OS_STRING "windows" /* On Windows, assume we're little endian. Even Itanium which has a @@ -518,6 +639,10 @@ #define DUK_USE_OS_STRING "qnx" #elif defined(DUK_F_TINSPIRE) /* --- TI-Nspire --- */ +#if defined(DUK_COMPILING_DUKTAPE) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE /* e.g. strptime */ +#endif + #define DUK_USE_DATE_NOW_GETTIMEOFDAY #define DUK_USE_DATE_TZO_GMTIME_R #define DUK_USE_DATE_PRS_STRPTIME @@ -531,13 +656,13 @@ #elif defined(DUK_F_EMSCRIPTEN) /* --- Emscripten --- */ #if defined(DUK_COMPILING_DUKTAPE) -#ifndef _POSIX_C_SOURCE +#if !defined(_POSIX_C_SOURCE) #define _POSIX_C_SOURCE 200809L #endif -#ifndef _GNU_SOURCE +#if !defined(_GNU_SOURCE) #define _GNU_SOURCE /* e.g. getdate_r */ #endif -#ifndef _XOPEN_SOURCE +#if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE /* e.g. strptime */ #endif #endif /* DUK_COMPILING_DUKTAPE */ @@ -559,16 +684,16 @@ #define DUK_USE_DATE_FMT_STRFTIME #define DUK_USE_OS_STRING "emscripten" -#elif defined(DUK_F_LINUX) -/* --- Linux --- */ +#elif defined(DUK_F_ANDROID) +/* --- Android --- */ #if defined(DUK_COMPILING_DUKTAPE) -#ifndef _POSIX_C_SOURCE +#if !defined(_POSIX_C_SOURCE) #define _POSIX_C_SOURCE 200809L #endif -#ifndef _GNU_SOURCE +#if !defined(_GNU_SOURCE) #define _GNU_SOURCE /* e.g. getdate_r */ #endif -#ifndef _XOPEN_SOURCE +#if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE /* e.g. strptime */ #endif #endif /* DUK_COMPILING_DUKTAPE */ @@ -589,6 +714,45 @@ #define DUK_USE_DATE_PRS_STRPTIME #define DUK_USE_DATE_FMT_STRFTIME +#if 0 /* XXX: safe condition? */ +#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME +#endif + +#define DUK_USE_OS_STRING "android" +#elif defined(DUK_F_LINUX) +/* --- Linux --- */ +#if defined(DUK_COMPILING_DUKTAPE) +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200809L +#endif +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE /* e.g. getdate_r */ +#endif +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE /* e.g. strptime */ +#endif +#endif /* DUK_COMPILING_DUKTAPE */ + +#include +#if defined(DUK_F_BCC) +/* no endian.h or stdint.h */ +#else +#include +#include +#endif /* DUK_F_BCC */ +#include +#include +#include + +#define DUK_USE_DATE_NOW_GETTIMEOFDAY +#define DUK_USE_DATE_TZO_GMTIME_R +#define DUK_USE_DATE_PRS_STRPTIME +#define DUK_USE_DATE_FMT_STRFTIME + +#if 0 /* XXX: safe condition? */ +#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME +#endif + #define DUK_USE_OS_STRING "linux" #elif defined(DUK_F_SUN) /* --- Solaris --- */ @@ -598,12 +762,50 @@ #define DUK_USE_DATE_FMT_STRFTIME #include -#include +#if defined(DUK_F_OLD_SOLARIS) +/* Old Solaris with no endian.h, stdint.h */ +#define DUK_F_NO_STDINT_H +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 3 +#endif +#else /* DUK_F_OLD_SOLARIS */ +#include +#endif /* DUK_F_OLD_SOLARIS */ + #include #include #include #define DUK_USE_OS_STRING "solaris" +#elif defined(DUK_F_AIX) +/* --- AIX --- */ +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 3 +#endif +#define DUK_USE_DATE_NOW_GETTIMEOFDAY +#define DUK_USE_DATE_TZO_GMTIME_R +#define DUK_USE_DATE_PRS_STRPTIME +#define DUK_USE_DATE_FMT_STRFTIME +#include +#include +#include + +#define DUK_USE_OS_STRING "aix" +#elif defined(DUK_F_HPUX) +/* --- HPUX --- */ +#define DUK_F_NO_STDINT_H +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 3 +#endif +#define DUK_USE_DATE_NOW_GETTIMEOFDAY +#define DUK_USE_DATE_TZO_GMTIME_R +#define DUK_USE_DATE_PRS_STRPTIME +#define DUK_USE_DATE_FMT_STRFTIME +#include +#include +#include + +#define DUK_USE_OS_STRING "hpux" #elif defined(DUK_F_POSIX) /* --- Generic POSIX --- */ #define DUK_USE_DATE_NOW_GETTIMEOFDAY @@ -688,9 +890,7 @@ #include #endif -#if defined(DUK_F_CPP) -#include /* std::exception */ -#endif +/* is only included if needed, based on DUK_USE_xxx flags. */ /* * Architecture autodetection @@ -702,13 +902,16 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 1 #endif -/* XXX: This is technically not guaranteed because it's possible to configure - * an x86 to require aligned accesses with Alignment Check (AC) flag. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 1 -#endif + #define DUK_USE_PACKED_TVAL + +/* FreeBSD, -m32, and clang prior to 5.0 has union aliasing issues which + * break duk_tval copying. Disable packed duk_tval automatically. + */ +#if defined(DUK_F_FREEBSD) && defined(DUK_F_X86) && \ + defined(__clang__) && defined(__clang_major__) && (__clang_major__ < 5) +#undef DUK_USE_PACKED_TVAL +#endif #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_X64) /* --- x64 --- */ @@ -716,12 +919,6 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 1 #endif -/* XXX: This is technically not guaranteed because it's possible to configure - * an x86 to require aligned accesses with Alignment Check (AC) flag. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 1 -#endif #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_X32) @@ -730,55 +927,30 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 1 #endif -/* XXX: This is technically not guaranteed because it's possible to configure - * an x86 to require aligned accesses with Alignment Check (AC) flag. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 1 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_ARM32) /* --- ARM 32-bit --- */ #define DUK_USE_ARCH_STRING "arm32" /* Byte order varies, so rely on autodetect. */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 4 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_ARM64) /* --- ARM 64-bit --- */ #define DUK_USE_ARCH_STRING "arm64" /* Byte order varies, so rely on autodetect. */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_MIPS32) /* --- MIPS 32-bit --- */ #define DUK_USE_ARCH_STRING "mips32" /* MIPS byte order varies so rely on autodetection. */ -/* Based on 'make checkalign' there are no alignment requirements on - * Linux MIPS except for doubles, which need align by 4. Alignment - * requirements vary based on target though. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 4 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_MIPS64) /* --- MIPS 64-bit --- */ #define DUK_USE_ARCH_STRING "mips64" /* MIPS byte order varies so rely on autodetection. */ -/* Good default is a bit arbitrary because alignment requirements - * depend on target. See https://github.com/svaarala/duktape/issues/102. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_PPC32) @@ -787,50 +959,42 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 3 #endif -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_PPC64) /* --- PowerPC 64-bit --- */ #define DUK_USE_ARCH_STRING "ppc64" -#if !defined(DUK_USE_BYTEORDER) -#define DUK_USE_BYTEORDER 3 -#endif -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif +/* No forced byteorder (both little and big endian are possible). */ #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_SPARC32) /* --- SPARC 32-bit --- */ #define DUK_USE_ARCH_STRING "sparc32" /* SPARC byte order varies so rely on autodetection. */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_SPARC64) /* --- SPARC 64-bit --- */ #define DUK_USE_ARCH_STRING "sparc64" /* SPARC byte order varies so rely on autodetection. */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif +#undef DUK_USE_PACKED_TVAL +#define DUK_F_PACKED_TVAL_PROVIDED +#elif defined(DUK_F_RISCV32) +/* --- RISC-V 32-bit --- */ +#define DUK_USE_ARCH_STRING "riscv32" +#define DUK_USE_BYTEORDER 1 +#define DUK_USE_PACKED_TVAL +#define DUK_F_PACKED_TVAL_PROVIDED +#elif defined(DUK_F_RISCV64) +/* --- RISC-V 64-bit --- */ +#define DUK_USE_ARCH_STRING "riscv64" +#define DUK_USE_BYTEORDER 1 #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_SUPERH) /* --- SuperH --- */ #define DUK_USE_ARCH_STRING "sh" /* Byte order varies, rely on autodetection. */ -/* Based on 'make checkalign' there are no alignment requirements on - * Linux SH4, but align by 4 is probably a good basic default. - */ -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 4 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_M68K) @@ -839,9 +1003,6 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 3 #endif -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #elif defined(DUK_F_EMSCRIPTEN) @@ -850,9 +1011,6 @@ #if !defined(DUK_USE_BYTEORDER) #define DUK_USE_BYTEORDER 1 #endif -#if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 8 -#endif #undef DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED #else @@ -887,6 +1045,11 @@ #define DUK_USE_BRANCH_HINTS #define DUK_LIKELY(x) __builtin_expect((x), 1) #define DUK_UNLIKELY(x) __builtin_expect((x), 0) +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_unpredictable) +#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) +#endif +#endif #if defined(DUK_F_C99) || defined(DUK_F_CPP11) #define DUK_NOINLINE __attribute__((noinline)) @@ -894,6 +1057,9 @@ #define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) #endif +/* DUK_HOT */ +/* DUK_COLD */ + #if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) /* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're * compiling Duktape or the application. @@ -965,8 +1131,20 @@ #define DUK_USE_FLEX_ZEROSIZE #endif -#undef DUK_USE_GCC_PRAGMAS +#define DUK_USE_CLANG_PRAGMAS #define DUK_USE_PACK_CLANG_ATTR + +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_bswap64) +#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) +#endif +#if __has_builtin(__builtin_bswap32) +#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) +#endif +#if __has_builtin(__builtin_bswap16) +#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) +#endif +#endif #elif defined(DUK_F_GCC) /* --- GCC --- */ #if defined(DUK_F_C99) || defined(DUK_F_CPP11) @@ -977,13 +1155,17 @@ #define DUK_VA_COPY(dest,src) __va_copy(dest,src) #endif -#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 20500L) -/* since gcc-2.5 */ +#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 20500L) && (DUK_F_GCC_VERSION < 50000L) +/* Since gcc-2.5. + * + * Disabled temporarily in GCC 5+ because of an unresolved noreturn-related + * issue: https://github.com/svaarala/duktape/issues/2155. + */ #define DUK_NORETURN(decl) decl __attribute__((noreturn)) #endif #if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40500L) -/* since gcc-4.5 */ +/* Since gcc-4.5. */ #define DUK_UNREACHABLE() do { __builtin_unreachable(); } while (0) #endif @@ -995,6 +1177,7 @@ #define DUK_LIKELY(x) __builtin_expect((x), 1) #define DUK_UNLIKELY(x) __builtin_expect((x), 0) #endif +/* XXX: equivalent of clang __builtin_unpredictable? */ #if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 30101) @@ -1003,6 +1186,12 @@ #define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) #endif +#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ + defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40300) +#define DUK_HOT __attribute__((hot)) +#define DUK_COLD __attribute__((cold)) +#endif + #if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) /* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're * compiling Duktape or the application. @@ -1082,6 +1271,7 @@ #define DUK_USE_FLEX_ZEROSIZE #endif +/* Since 4.6 one can '#pragma GCC diagnostic push/pop'. */ #if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40600) #define DUK_USE_GCC_PRAGMAS #else @@ -1089,6 +1279,16 @@ #endif #define DUK_USE_PACK_GCC_ATTR + +/* Availability varies based on platform (between GCC 4.4 and 4.8), and there + * are apparently some bugs in GCC 4.x. Check for GCC 5.0 before enabling + * these to be safe. + */ +#if defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 50000L) +#define DUK_BSWAP64(x) ((duk_uint64_t) __builtin_bswap64((duk_uint64_t) (x))) +#define DUK_BSWAP32(x) ((duk_uint32_t) __builtin_bswap32((duk_uint32_t) (x))) +#define DUK_BSWAP16(x) ((duk_uint16_t) __builtin_bswap16((duk_uint16_t) (x))) +#endif #elif defined(DUK_F_MSVC) /* --- MSVC --- */ /* http://msdn.microsoft.com/en-us/library/aa235362(VS.60).aspx */ @@ -1181,6 +1381,18 @@ #define DUK_SNPRINTF _snprintf #define DUK_VSNPRINTF _vsnprintf #endif + +/* Avoid warning when doing DUK_UNREF(some_function). */ +#if defined(_MSC_VER) && (_MSC_VER < 1500) +#pragma warning(disable: 4100 4101 4550 4551) +#define DUK_UNREF(x) +#else +#define DUK_UNREF(x) do { __pragma(warning(suppress:4100 4101 4550 4551)) (x); } while (0) +#endif + +/* Older versions of MSVC don't support the LL/ULL suffix. */ +#define DUK_U64_CONSTANT(x) x##ui64 +#define DUK_I64_CONSTANT(x) x##i64 #elif defined(DUK_F_EMSCRIPTEN) /* --- Emscripten --- */ #define DUK_NORETURN(decl) decl __attribute__((noreturn)) @@ -1194,6 +1406,11 @@ #define DUK_USE_BRANCH_HINTS #define DUK_LIKELY(x) __builtin_expect((x), 1) #define DUK_UNLIKELY(x) __builtin_expect((x), 0) +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_unpredictable) +#define DUK_UNPREDICTABLE(x) __builtin_unpredictable((x)) +#endif +#endif #if defined(DUK_F_C99) || defined(DUK_F_CPP11) #define DUK_NOINLINE __attribute__((noinline)) @@ -1401,10 +1618,16 @@ #if defined(DUK_F_X86) || defined(DUK_F_X32) || \ defined(DUK_F_M68K) || defined(DUK_F_PPC32) || \ defined(DUK_F_BCC) || \ - (defined(__WORDSIZE) && (__WORDSIZE == 32)) + (defined(__WORDSIZE) && (__WORDSIZE == 32)) || \ + ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ + defined(DUK_F_HPUX)) && defined(_ILP32)) || \ + defined(DUK_F_ARM32) #define DUK_F_32BIT_PTRS #elif defined(DUK_F_X64) || \ - (defined(__WORDSIZE) && (__WORDSIZE == 64)) + (defined(__WORDSIZE) && (__WORDSIZE == 64)) || \ + ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \ + defined(DUK_F_HPUX)) && defined(_LP64)) || \ + defined(DUK_F_ARM64) #define DUK_F_64BIT_PTRS #else /* not sure, not needed with C99 anyway */ @@ -1607,13 +1830,16 @@ typedef unsigned long long duk_uint64_t; typedef signed long long duk_int64_t; #endif #endif -#if !defined(DUK_F_HAVE_64BIT) && \ - (defined(DUK_F_MINGW) || defined(DUK_F_MSVC)) -/* Both MinGW and MSVC have a 64-bit type. */ +#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MINGW) #define DUK_F_HAVE_64BIT typedef unsigned long duk_uint64_t; typedef signed long duk_int64_t; #endif +#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MSVC) +#define DUK_F_HAVE_64BIT +typedef unsigned __int64 duk_uint64_t; +typedef signed __int64 duk_int64_t; +#endif #if !defined(DUK_F_HAVE_64BIT) /* cannot detect 64-bit type, not always needed so don't error */ #endif @@ -1821,10 +2047,10 @@ typedef duk_uint_fast16_t duk_small_uint_fast_t; #define DUK_SMALL_UINT_FAST_MIN DUK_UINT_FAST16_MIN #define DUK_SMALL_UINT_FAST_MAX DUK_UINT_FAST16_MAX -/* Boolean values are represented with the platform 'int'. */ -typedef duk_small_int_t duk_bool_t; -#define DUK_BOOL_MIN DUK_SMALL_INT_MIN -#define DUK_BOOL_MAX DUK_SMALL_INT_MAX +/* Boolean values are represented with the platform 'unsigned int'. */ +typedef duk_small_uint_t duk_bool_t; +#define DUK_BOOL_MIN DUK_SMALL_UINT_MIN +#define DUK_BOOL_MAX DUK_SMALL_UINT_MAX /* Index values must have at least 32-bit signed range. */ typedef duk_int_t duk_idx_t; @@ -1893,7 +2119,10 @@ typedef double duk_double_t; #endif #endif -/* Type for public API calls. */ +/* Type used in public API declarations and user code. Typedef maps to + * 'struct duk_hthread' like the 'duk_hthread' typedef which is used + * exclusively in internals. + */ typedef struct duk_hthread duk_context; /* Check whether we should use 64-bit integers or not. @@ -1912,6 +2141,11 @@ typedef struct duk_hthread duk_context; * Fill-ins for platform, architecture, and compiler */ +/* An abort()-like primitive is needed by the default fatal error handler. */ +#if !defined(DUK_ABORT) +#define DUK_ABORT abort +#endif + #if !defined(DUK_SETJMP) #define DUK_JMPBUF_TYPE jmp_buf #define DUK_SETJMP(jb) setjmp((jb)) @@ -1925,17 +2159,6 @@ typedef struct duk_hthread duk_context; #define DUK_LONGJMP(jb) siglongjmp((jb), 1) #endif -typedef FILE duk_file; -#if !defined(DUK_STDIN) -#define DUK_STDIN stdin -#endif -#if !defined(DUK_STDOUT) -#define DUK_STDOUT stdout -#endif -#if !defined(DUK_STDERR) -#define DUK_STDERR stderr -#endif - /* Special naming to avoid conflict with e.g. DUK_FREE() in duk_heap.h * (which is unfortunately named). May sometimes need replacement, e.g. * some compilers don't handle zero length or NULL correctly in realloc(). @@ -2002,12 +2225,6 @@ typedef FILE duk_file; #if !defined(DUK_STRNCMP) #define DUK_STRNCMP strncmp #endif -#if !defined(DUK_PRINTF) -#define DUK_PRINTF printf -#endif -#if !defined(DUK_FPRINTF) -#define DUK_FPRINTF fprintf -#endif #if !defined(DUK_SPRINTF) #define DUK_SPRINTF sprintf #endif @@ -2028,46 +2245,9 @@ typedef FILE duk_file; #if !defined(DUK_VSSCANF) #define DUK_VSSCANF vsscanf #endif -#if !defined(DUK_FOPEN) -#define DUK_FOPEN fopen -#endif -#if !defined(DUK_FCLOSE) -#define DUK_FCLOSE fclose -#endif -#if !defined(DUK_FREAD) -#define DUK_FREAD fread -#endif -#if !defined(DUK_FWRITE) -#define DUK_FWRITE fwrite -#endif -#if !defined(DUK_FSEEK) -#define DUK_FSEEK fseek -#endif -#if !defined(DUK_FTELL) -#define DUK_FTELL ftell -#endif -#if !defined(DUK_FFLUSH) -#define DUK_FFLUSH fflush -#endif -#if !defined(DUK_FPUTC) -#define DUK_FPUTC fputc -#endif #if !defined(DUK_MEMZERO) #define DUK_MEMZERO(p,n) DUK_MEMSET((p), 0, (n)) #endif -#if !defined(DUK_ABORT) -#define DUK_ABORT abort -#endif -#if !defined(DUK_EXIT) -#define DUK_EXIT exit -#endif - -#if !defined(DUK_DOUBLE_2TO32) -#define DUK_DOUBLE_2TO32 4294967296.0 -#endif -#if !defined(DUK_DOUBLE_2TO31) -#define DUK_DOUBLE_2TO31 2147483648.0 -#endif #if !defined(DUK_DOUBLE_INFINITY) #undef DUK_USE_COMPUTED_INFINITY @@ -2076,7 +2256,8 @@ typedef FILE duk_file; #define DUK_DOUBLE_INFINITY (__builtin_inf()) #elif defined(INFINITY) #define DUK_DOUBLE_INFINITY ((double) INFINITY) -#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) +#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ + !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) #define DUK_DOUBLE_INFINITY (1.0 / 0.0) #else /* In VBCC (1.0 / 0.0) results in a warning and 0.0 instead of infinity. @@ -2092,7 +2273,8 @@ typedef FILE duk_file; #undef DUK_USE_COMPUTED_NAN #if defined(NAN) #define DUK_DOUBLE_NAN NAN -#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) +#elif !defined(DUK_F_VBCC) && !defined(DUK_F_MSVC) && !defined(DUK_F_BCC) && \ + !defined(DUK_F_OLD_SOLARIS) && !defined(DUK_F_AIX) #define DUK_DOUBLE_NAN (0.0 / 0.0) #else /* In VBCC (0.0 / 0.0) results in a warning and 0.0 instead of NaN. @@ -2141,6 +2323,9 @@ typedef FILE duk_file; * To be safe, use replacements. */ #define DUK_F_USE_REPL_ALL +#elif defined(DUK_F_AIX) +/* Older versions may be missing isnan(), etc. */ +#define DUK_F_USE_REPL_ALL #endif #if defined(DUK_F_USE_REPL_ALL) @@ -2176,29 +2361,6 @@ typedef FILE duk_file; #undef DUK_F_USE_REPL_ALL #endif -/* Some math functions are C99 only. This is also an issue with some - * embedded environments using uclibc where uclibc has been configured - * not to provide some functions. For now, use replacements whenever - * using uclibc. - */ -#undef DUK_USE_MATH_FMIN -#undef DUK_USE_MATH_FMAX -#undef DUK_USE_MATH_ROUND -#if defined(DUK_F_UCLIBC) -/* uclibc may be missing these */ -#elif defined(DUK_F_AMIGAOS) && defined(DUK_F_VBCC) -/* vbcc + AmigaOS may be missing these */ -#elif defined(DUK_F_MINT) -/* mint clib is missing these */ -#elif !defined(DUK_F_C99) && !defined(DUK_F_CPP11) -/* build is not C99 or C++11, play it safe */ -#else -/* C99 or C++11, no known issues */ -#define DUK_USE_MATH_FMIN -#define DUK_USE_MATH_FMAX -#define DUK_USE_MATH_ROUND -#endif - /* These functions don't currently need replacement but are wrapped for * completeness. Because these are used as function pointers, they need * to be defined as concrete C functions (not macros). @@ -2206,12 +2368,6 @@ typedef FILE duk_file; #if !defined(DUK_FABS) #define DUK_FABS fabs #endif -#if !defined(DUK_FMIN) -#define DUK_FMIN fmin -#endif -#if !defined(DUK_FMAX) -#define DUK_FMAX fmax -#endif #if !defined(DUK_FLOOR) #define DUK_FLOOR floor #endif @@ -2255,13 +2411,43 @@ typedef FILE duk_file; #define DUK_SQRT sqrt #endif -/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics, - * see test-bug-netbsd-math-pow.js. Use NetBSD specific workaround. - * (This might be a wider problem; if so, generalize the define name.) +/* The functions below exist only in C99/C++11 or later and need a workaround + * for platforms that don't include them. MSVC isn't detected as C99, but + * these functions also exist in MSVC 2013 and later so include a clause for + * that too. Android doesn't have log2; disable all of these for Android. */ -#undef DUK_USE_POW_NETBSD_WORKAROUND -#if defined(DUK_F_NETBSD) -#define DUK_USE_POW_NETBSD_WORKAROUND +#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && \ + !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT) +#if !defined(DUK_CBRT) +#define DUK_CBRT cbrt +#endif +#if !defined(DUK_LOG2) +#define DUK_LOG2 log2 +#endif +#if !defined(DUK_LOG10) +#define DUK_LOG10 log10 +#endif +#if !defined(DUK_TRUNC) +#define DUK_TRUNC trunc +#endif +#endif /* DUK_F_C99 etc */ + +/* NetBSD 6.0 x86 (at least) has a few problems with pow() semantics, + * see test-bug-netbsd-math-pow.js. MinGW has similar (but different) + * issues, see test-bug-mingw-math-issues.js. Enable pow() workarounds + * for these targets. + */ +#undef DUK_USE_POW_WORKAROUNDS +#if defined(DUK_F_NETBSD) || defined(DUK_F_MINGW) +#define DUK_USE_POW_WORKAROUNDS +#endif + +/* Similar workarounds for atan2() semantics issues. MinGW issues are + * documented in test-bug-mingw-math-issues.js. + */ +#undef DUK_USE_ATAN2_WORKAROUNDS +#if defined(DUK_F_MINGW) +#define DUK_USE_ATAN2_WORKAROUNDS #endif /* Rely as little as possible on compiler behavior for NaN comparison, @@ -2304,25 +2490,6 @@ typedef FILE duk_file; * byte order for doubles is referred to as "mixed endian". */ -/* For custom platforms allow user to define byteorder explicitly. - * Since endianness headers are not standardized, this is a useful - * workaround for custom platforms for which endianness detection - * is not directly supported. Perhaps custom hardware is used and - * user cannot submit upstream patches. - */ -#if defined(DUK_OPT_FORCE_BYTEORDER) -#undef DUK_USE_BYTEORDER -#if (DUK_OPT_FORCE_BYTEORDER == 1) -#define DUK_USE_BYTEORDER 1 -#elif (DUK_OPT_FORCE_BYTEORDER == 2) -#define DUK_USE_BYTEORDER 2 -#elif (DUK_OPT_FORCE_BYTEORDER == 3) -#define DUK_USE_BYTEORDER 3 -#else -#error invalid DUK_OPT_FORCE_BYTEORDER value -#endif -#endif /* DUK_OPT_FORCE_BYTEORDER */ - /* GCC and Clang provide endianness defines as built-in predefines, with * leading and trailing double underscores (e.g. __BYTE_ORDER__). See * output of "make gccpredefs" and "make clangpredefs". Clang doesn't @@ -2417,27 +2584,18 @@ typedef FILE duk_file; * * Assume unaligned accesses are not supported unless specifically allowed * in the target platform. Some platforms may support unaligned accesses - * but alignment to 4 or 8 may still be desirable. + * but alignment to 4 or 8 may still be desirable. Note that unaligned + * accesses (and even pointers) relative to natural alignment (regardless + * of target alignment) are technically undefined behavior and thus + * compiler/architecture specific. */ -/* If not provided, use safe default for alignment. */ +/* If not forced, use safe default for alignment. */ #if !defined(DUK_USE_ALIGN_BY) #define DUK_USE_ALIGN_BY 8 #endif -/* User forced alignment to 4 or 8. */ -#if defined(DUK_OPT_FORCE_ALIGN) -#undef DUK_USE_ALIGN_BY -#if (DUK_OPT_FORCE_ALIGN == 4) -#define DUK_USE_ALIGN_BY 4 -#elif (DUK_OPT_FORCE_ALIGN == 8) -#define DUK_USE_ALIGN_BY 8 -#else -#error invalid DUK_OPT_FORCE_ALIGN value -#endif -#endif - -/* Compiler specific hackery needed to force struct size to match aligment, +/* Compiler specific hackery needed to force struct size to match alignment, * see e.g. duk_hbuffer.h. * * http://stackoverflow.com/questions/11130109/c-struct-size-alignment @@ -2448,6 +2606,13 @@ typedef FILE duk_file; #define DUK_USE_PACK_DUMMY_MEMBER #endif +#if !defined(DUK_U64_CONSTANT) +#define DUK_U64_CONSTANT(x) x##ULL +#endif +#if !defined(DUK_I64_CONSTANT) +#define DUK_I64_CONSTANT(x) x##LL +#endif + #if !defined(DUK_VA_COPY) /* We need va_copy() which is defined in C99 / C++11, so an awkward * replacement is needed for pre-C99 / pre-C++11 environments. This @@ -2479,30 +2644,50 @@ typedef FILE duk_file; #endif #if !defined(DUK_CAUSE_SEGFAULT) -/* This is optionally used by panic handling to cause the program to segfault - * (instead of e.g. abort()) on panic. Valgrind will then indicate the C - * call stack leading to the panic. +/* This can be used for testing; valgrind will then indicate the C call stack + * leading to the call site. */ #define DUK_CAUSE_SEGFAULT() do { *((volatile duk_uint32_t *) NULL) = (duk_uint32_t) 0xdeadbeefUL; } while (0) #endif + #if !defined(DUK_UNREF) /* Macro for suppressing warnings for potentially unreferenced variables. * The variables can be actually unreferenced or unreferenced in some * specific cases only; for instance, if a variable is only debug printed, - * it is unreferenced when debug printing is disabled. + * it is unreferenced when debug printing is disabled. May cause warnings + * for volatile arguments. */ #define DUK_UNREF(x) do { (void) (x); } while (0) #endif -#if !defined(DUK_NORETURN) + +/* Fillin for DUK_NORETURN; DUK_WO_NORETURN() is used to insert dummy + * dummy statements after noreturn calls to silence harmless compiler + * warnings, e.g.: + * + * DUK_ERROR_TYPE(thr, "aiee"); + * DUK_WO_NORETURN(return 0;); + * + * Statements inside DUK_WO_NORETURN() must NEVER be actually reachable, + * and they're only included to satisfy the compiler. + */ +#if defined(DUK_NORETURN) +#define DUK_WO_NORETURN(stmt) do { } while (0) +#else #define DUK_NORETURN(decl) decl +#define DUK_WO_NORETURN(stmt) do { stmt } while (0) #endif -#if !defined(DUK_UNREACHABLE) + +#if defined(DUK_UNREACHABLE) +#define DUK_WO_UNREACHABLE(stmt) do { } while (0) +#else /* Don't know how to declare unreachable point, so don't do it; this * may cause some spurious compilation warnings (e.g. "variable used * uninitialized"). */ #define DUK_UNREACHABLE() do { } while (0) +#define DUK_WO_UNREACHABLE(stmt) do { stmt } while (0) #endif + #if !defined(DUK_LOSE_CONST) /* Convert any input pointer into a "void *", losing a const qualifier. * This is not fully portable because casting through duk_uintptr_t may @@ -2517,6 +2702,9 @@ typedef FILE duk_file; #if !defined(DUK_UNLIKELY) #define DUK_UNLIKELY(x) (x) #endif +#if !defined(DUK_UNPREDICTABLE) +#define DUK_UNPREDICTABLE(x) (x) +#endif #if !defined(DUK_NOINLINE) #define DUK_NOINLINE /*nop*/ @@ -2528,6 +2716,13 @@ typedef FILE duk_file; #define DUK_ALWAYS_INLINE /*nop*/ #endif +#if !defined(DUK_HOT) +#define DUK_HOT /*nop*/ +#endif +#if !defined(DUK_COLD) +#define DUK_COLD /*nop*/ +#endif + #if !defined(DUK_EXTERNAL_DECL) #define DUK_EXTERNAL_DECL extern #endif @@ -2571,17 +2766,30 @@ typedef FILE duk_file; #endif #endif +#if defined(DUK_F_HAVE_64BIT) +#if !defined(DUK_BSWAP64) +#define DUK_BSWAP64(x) \ + ((((duk_uint64_t) (x)) >> 56U) | \ + ((((duk_uint64_t) (x)) >> 40U) & DUK_U64_CONSTANT(0xff00)) | \ + ((((duk_uint64_t) (x)) >> 24U) & DUK_U64_CONSTANT(0xff0000)) | \ + ((((duk_uint64_t) (x)) >> 8U) & DUK_U64_CONSTANT(0xff000000)) | \ + ((((duk_uint64_t) (x)) << 8U) & DUK_U64_CONSTANT(0xff00000000)) | \ + ((((duk_uint64_t) (x)) << 24U) & DUK_U64_CONSTANT(0xff0000000000)) | \ + ((((duk_uint64_t) (x)) << 40U) & DUK_U64_CONSTANT(0xff000000000000)) | \ + (((duk_uint64_t) (x)) << 56U)) +#endif +#endif #if !defined(DUK_BSWAP32) #define DUK_BSWAP32(x) \ - ((((duk_uint32_t) (x)) >> 24) | \ - ((((duk_uint32_t) (x)) >> 8) & 0xff00UL) | \ - ((((duk_uint32_t) (x)) << 8) & 0xff0000UL) | \ - (((duk_uint32_t) (x)) << 24)) + ((((duk_uint32_t) (x)) >> 24U) | \ + ((((duk_uint32_t) (x)) >> 8U) & 0xff00UL) | \ + ((((duk_uint32_t) (x)) << 8U) & 0xff0000UL) | \ + (((duk_uint32_t) (x)) << 24U)) #endif #if !defined(DUK_BSWAP16) #define DUK_BSWAP16(x) \ - ((duk_uint16_t) (x) >> 8) | \ - ((duk_uint16_t) (x) << 8) + ((duk_uint16_t) (x) >> 8U) | \ + ((duk_uint16_t) (x) << 8U) #endif /* DUK_USE_VARIADIC_MACROS: required from compilers, so no fill-in. */ @@ -2653,17 +2861,9 @@ typedef FILE duk_file; #if defined(DUK_F_PACKED_TVAL_POSSIBLE) #define DUK_USE_PACKED_TVAL #endif - #undef DUK_F_PACKED_TVAL_POSSIBLE -#endif /* DUK_F_PACKED_TVAL_PROVIDED */ -/* Feature option forcing. */ -#if defined(DUK_OPT_NO_PACKED_TVAL) -#undef DUK_USE_PACKED_TVAL -#elif defined(DUK_OPT_PACKED_TVAL) -#undef DUK_USE_PACKED_TVAL -#define DUK_USE_PACKED_TVAL -#endif +#endif /* DUK_F_PACKED_TVAL_PROVIDED */ /* Object property allocation layout has implications for memory and code * footprint and generated code size/speed. The best layout also depends * on whether the platform has alignment requirements or benefits from @@ -2693,853 +2893,201 @@ typedef FILE duk_file; #error __FAST_MATH__ defined, refusing to compile #endif -/* - * Feature option handling - */ - -#if !defined(DUK_USE_ALIGN_BY) -#if defined(DUK_OPT_FORCE_ALIGN) -#define DUK_USE_ALIGN_BY DUK_OPT_FORCE_ALIGN -#else -#define DUK_USE_ALIGN_BY 8 -#endif -#endif - -#if defined(DUK_OPT_ASSERTIONS) -#define DUK_USE_ASSERTIONS -#elif defined(DUK_OPT_NO_ASSERTIONS) -#undef DUK_USE_ASSERTIONS -#else -#undef DUK_USE_ASSERTIONS -#endif - -#if defined(DUK_OPT_AUGMENT_ERRORS) -#define DUK_USE_AUGMENT_ERROR_CREATE -#elif defined(DUK_OPT_NO_AUGMENT_ERRORS) -#undef DUK_USE_AUGMENT_ERROR_CREATE -#else -#define DUK_USE_AUGMENT_ERROR_CREATE -#endif - -#if defined(DUK_OPT_AUGMENT_ERRORS) -#define DUK_USE_AUGMENT_ERROR_THROW -#elif defined(DUK_OPT_NO_AUGMENT_ERRORS) -#undef DUK_USE_AUGMENT_ERROR_THROW -#else -#define DUK_USE_AUGMENT_ERROR_THROW -#endif - -#if defined(DUK_OPT_BROWSER_LIKE) -#define DUK_USE_BROWSER_LIKE -#elif defined(DUK_OPT_NO_BROWSER_LIKE) -#undef DUK_USE_BROWSER_LIKE -#else -#define DUK_USE_BROWSER_LIKE -#endif - -#if defined(DUK_OPT_BUFFEROBJECT_SUPPORT) -#define DUK_USE_BUFFEROBJECT_SUPPORT -#elif defined(DUK_OPT_NO_BUFFEROBJECT_SUPPORT) -#undef DUK_USE_BUFFEROBJECT_SUPPORT -#else -#define DUK_USE_BUFFEROBJECT_SUPPORT -#endif - -#if defined(DUK_OPT_BUFLEN16) -#define DUK_USE_BUFLEN16 -#elif defined(DUK_OPT_NO_BUFLEN16) -#undef DUK_USE_BUFLEN16 -#else -#undef DUK_USE_BUFLEN16 -#endif - -#if defined(DUK_OPT_BYTECODE_DUMP_SUPPORT) -#define DUK_USE_BYTECODE_DUMP_SUPPORT -#elif defined(DUK_OPT_NO_BYTECODE_DUMP_SUPPORT) -#undef DUK_USE_BYTECODE_DUMP_SUPPORT -#else -#define DUK_USE_BYTECODE_DUMP_SUPPORT -#endif - -#if defined(DUK_OPT_COMMONJS_MODULES) -#define DUK_USE_COMMONJS_MODULES -#elif defined(DUK_OPT_NO_COMMONJS_MODULES) -#undef DUK_USE_COMMONJS_MODULES -#else -#define DUK_USE_COMMONJS_MODULES -#endif - -#if defined(DUK_OPT_CPP_EXCEPTIONS) -#define DUK_USE_CPP_EXCEPTIONS -#elif defined(DUK_OPT_NO_CPP_EXCEPTIONS) -#undef DUK_USE_CPP_EXCEPTIONS -#else -#undef DUK_USE_CPP_EXCEPTIONS -#endif - -#if defined(DUK_OPT_DATAPTR16) -#define DUK_USE_DATAPTR16 -#elif defined(DUK_OPT_NO_DATAPTR16) -#undef DUK_USE_DATAPTR16 -#else -#undef DUK_USE_DATAPTR16 -#endif - -#if defined(DUK_OPT_DATAPTR_DEC16) -#define DUK_USE_DATAPTR_DEC16(udata,ptr) DUK_OPT_DATAPTR_DEC16((udata),(ptr)) -#else -#undef DUK_USE_DATAPTR_DEC16 -#endif - -#if defined(DUK_OPT_DATAPTR_ENC16) -#define DUK_USE_DATAPTR_ENC16(udata,ptr) DUK_OPT_DATAPTR_ENC16((udata),(ptr)) -#else -#undef DUK_USE_DATAPTR_ENC16 -#endif - -#if defined(DUK_OPT_DDDPRINT) -#define DUK_USE_DDDPRINT -#elif defined(DUK_OPT_NO_DDDPRINT) -#undef DUK_USE_DDDPRINT -#else -#undef DUK_USE_DDDPRINT -#endif - -#if defined(DUK_OPT_DDPRINT) -#define DUK_USE_DDPRINT -#elif defined(DUK_OPT_NO_DDPRINT) -#undef DUK_USE_DDPRINT -#else -#undef DUK_USE_DDPRINT -#endif - -#if defined(DUK_OPT_DEBUG) -#define DUK_USE_DEBUG -#elif defined(DUK_OPT_NO_DEBUG) -#undef DUK_USE_DEBUG -#else -#undef DUK_USE_DEBUG -#endif - -#if defined(DUK_OPT_DEBUGGER_DUMPHEAP) -#define DUK_USE_DEBUGGER_DUMPHEAP -#elif defined(DUK_OPT_NO_DEBUGGER_DUMPHEAP) -#undef DUK_USE_DEBUGGER_DUMPHEAP -#else -#undef DUK_USE_DEBUGGER_DUMPHEAP -#endif - -#if defined(DUK_OPT_DEBUGGER_FWD_LOGGING) -#define DUK_USE_DEBUGGER_FWD_LOGGING -#elif defined(DUK_OPT_NO_DEBUGGER_FWD_LOGGING) -#undef DUK_USE_DEBUGGER_FWD_LOGGING -#else -#undef DUK_USE_DEBUGGER_FWD_LOGGING -#endif - -#if defined(DUK_OPT_DEBUGGER_FWD_PRINTALERT) -#define DUK_USE_DEBUGGER_FWD_PRINTALERT -#elif defined(DUK_OPT_NO_DEBUGGER_FWD_PRINTALERT) -#undef DUK_USE_DEBUGGER_FWD_PRINTALERT -#else -#undef DUK_USE_DEBUGGER_FWD_PRINTALERT -#endif - -#if defined(DUK_OPT_DEBUGGER_INSPECT) -#define DUK_USE_DEBUGGER_INSPECT -#elif defined(DUK_OPT_NO_DEBUGGER_INSPECT) -#undef DUK_USE_DEBUGGER_INSPECT -#else -#undef DUK_USE_DEBUGGER_INSPECT -#endif - -#if defined(DUK_OPT_DEBUGGER_PAUSE_UNCAUGHT) -#define DUK_USE_DEBUGGER_PAUSE_UNCAUGHT -#elif defined(DUK_OPT_NO_DEBUGGER_PAUSE_UNCAUGHT) -#undef DUK_USE_DEBUGGER_PAUSE_UNCAUGHT -#else -#undef DUK_USE_DEBUGGER_PAUSE_UNCAUGHT -#endif - -#if defined(DUK_OPT_DEBUGGER_SUPPORT) -#define DUK_USE_DEBUGGER_SUPPORT -#elif defined(DUK_OPT_NO_DEBUGGER_SUPPORT) -#undef DUK_USE_DEBUGGER_SUPPORT -#else -#undef DUK_USE_DEBUGGER_SUPPORT -#endif - -#if defined(DUK_OPT_DEBUGGER_THROW_NOTIFY) -#define DUK_USE_DEBUGGER_THROW_NOTIFY -#elif defined(DUK_OPT_NO_DEBUGGER_THROW_NOTIFY) -#undef DUK_USE_DEBUGGER_THROW_NOTIFY -#else -#define DUK_USE_DEBUGGER_THROW_NOTIFY -#endif - -#if defined(DUK_OPT_DEBUGGER_TRANSPORT_TORTURE) -#define DUK_USE_DEBUGGER_TRANSPORT_TORTURE -#elif defined(DUK_OPT_NO_DEBUGGER_TRANSPORT_TORTURE) -#undef DUK_USE_DEBUGGER_TRANSPORT_TORTURE -#else -#undef DUK_USE_DEBUGGER_TRANSPORT_TORTURE -#endif - -#if defined(DUK_OPT_DEBUG_BUFSIZE) -#define DUK_USE_DEBUG_BUFSIZE DUK_OPT_DEBUG_BUFSIZE -#else -#define DUK_USE_DEBUG_BUFSIZE 65536L -#endif - -#if defined(DUK_OPT_REFERENCE_COUNTING) -#define DUK_USE_DOUBLE_LINKED_HEAP -#elif defined(DUK_OPT_NO_REFERENCE_COUNTING) -#undef DUK_USE_DOUBLE_LINKED_HEAP -#else -#define DUK_USE_DOUBLE_LINKED_HEAP -#endif - -#if defined(DUK_OPT_DPRINT) -#define DUK_USE_DPRINT -#elif defined(DUK_OPT_NO_DPRINT) -#undef DUK_USE_DPRINT -#else -#undef DUK_USE_DPRINT -#endif - -#if defined(DUK_OPT_DPRINT_COLORS) -#define DUK_USE_DPRINT_COLORS -#elif defined(DUK_OPT_NO_DPRINT_COLORS) -#undef DUK_USE_DPRINT_COLORS -#else -#undef DUK_USE_DPRINT_COLORS -#endif - -#if defined(DUK_OPT_DPRINT_RDTSC) -#define DUK_USE_DPRINT_RDTSC -#elif defined(DUK_OPT_NO_DPRINT_RDTSC) -#undef DUK_USE_DPRINT_RDTSC -#else -#undef DUK_USE_DPRINT_RDTSC -#endif - -#if defined(DUK_OPT_AUGMENT_ERRORS) -#define DUK_USE_ERRCREATE -#elif defined(DUK_OPT_NO_AUGMENT_ERRORS) -#undef DUK_USE_ERRCREATE -#else -#define DUK_USE_ERRCREATE -#endif - -#if defined(DUK_OPT_AUGMENT_ERRORS) -#define DUK_USE_ERRTHROW -#elif defined(DUK_OPT_NO_AUGMENT_ERRORS) -#undef DUK_USE_ERRTHROW -#else -#define DUK_USE_ERRTHROW -#endif - -#if defined(DUK_OPT_ES6_OBJECT_PROTO_PROPERTY) -#define DUK_USE_ES6_OBJECT_PROTO_PROPERTY -#elif defined(DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY) -#undef DUK_USE_ES6_OBJECT_PROTO_PROPERTY -#else -#define DUK_USE_ES6_OBJECT_PROTO_PROPERTY -#endif - -#if defined(DUK_OPT_ES6_OBJECT_SETPROTOTYPEOF) -#define DUK_USE_ES6_OBJECT_SETPROTOTYPEOF -#elif defined(DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF) -#undef DUK_USE_ES6_OBJECT_SETPROTOTYPEOF -#else -#define DUK_USE_ES6_OBJECT_SETPROTOTYPEOF -#endif - -#if defined(DUK_OPT_ES6_PROXY) -#define DUK_USE_ES6_PROXY -#elif defined(DUK_OPT_NO_ES6_PROXY) -#undef DUK_USE_ES6_PROXY -#else -#define DUK_USE_ES6_PROXY -#endif - -#if defined(DUK_OPT_ES6_REGEXP_BRACES) -#define DUK_USE_ES6_REGEXP_BRACES -#elif defined(DUK_OPT_NO_ES6_REGEXP_BRACES) -#undef DUK_USE_ES6_REGEXP_BRACES -#else -#define DUK_USE_ES6_REGEXP_BRACES -#endif - -#undef DUK_USE_EXEC_INDIRECT_BOUND_CHECK -#if defined(DUK_OPT_DEBUG) || defined(DUK_OPT_ASSERTIONS) -/* Enabled with debug/assertions just so that any issues can be caught. */ -#define DUK_USE_EXEC_INDIRECT_BOUND_CHECK -#endif - -#undef DUK_USE_EXEC_TIMEOUT_CHECK -#if defined(DUK_OPT_EXEC_TIMEOUT_CHECK) -#define DUK_USE_EXEC_TIMEOUT_CHECK(udata) DUK_OPT_EXEC_TIMEOUT_CHECK((udata)) -#endif - -#undef DUK_USE_EXTSTR_FREE -#if defined(DUK_OPT_EXTERNAL_STRINGS) && defined(DUK_OPT_EXTSTR_FREE) -#define DUK_USE_EXTSTR_FREE(udata,ptr) DUK_OPT_EXTSTR_FREE((udata), (ptr)) -#endif - -#undef DUK_USE_EXTSTR_INTERN_CHECK -#if defined(DUK_OPT_EXTERNAL_STRINGS) && defined(DUK_OPT_EXTSTR_INTERN_CHECK) -#define DUK_USE_EXTSTR_INTERN_CHECK(udata,ptr,len) DUK_OPT_EXTSTR_INTERN_CHECK((udata), (ptr), (len)) -#endif - -/* Support for 48-bit signed integer duk_tval with transparent semantics. */ -#undef DUK_USE_FASTINT -#if defined(DUK_OPT_FASTINT) -#if !defined(DUK_F_HAVE_64BIT) -#error DUK_OPT_FASTINT requires 64-bit integer type support at the moment -#endif -#define DUK_USE_FASTINT -#endif - -#if defined(DUK_OPT_FILE_IO) -#define DUK_USE_FILE_IO -#elif defined(DUK_OPT_NO_FILE_IO) -#undef DUK_USE_FILE_IO -#else -#define DUK_USE_FILE_IO -#endif - -#if defined(DUK_OPT_FUNCPTR16) -#define DUK_USE_FUNCPTR16 -#elif defined(DUK_OPT_NO_FUNCPTR16) -#undef DUK_USE_FUNCPTR16 -#else -#undef DUK_USE_FUNCPTR16 -#endif - -#if defined(DUK_OPT_FUNCPTR_DEC16) -#define DUK_USE_FUNCPTR_DEC16(udata,ptr) DUK_OPT_FUNCPTR_DEC16((udata),(ptr)) -#else -#undef DUK_USE_FUNCPTR_DEC16 -#endif - -#if defined(DUK_OPT_FUNCPTR_ENC16) -#define DUK_USE_FUNCPTR_ENC16(udata,ptr) DUK_OPT_FUNCPTR_ENC16((udata),(ptr)) -#else -#undef DUK_USE_FUNCPTR_ENC16 -#endif - -#if defined(DUK_OPT_GC_TORTURE) -#define DUK_USE_GC_TORTURE -#elif defined(DUK_OPT_NO_GC_TORTURE) -#undef DUK_USE_GC_TORTURE -#else -#undef DUK_USE_GC_TORTURE -#endif - -#if defined(DUK_OPT_HEAPPTR16) -#define DUK_USE_HEAPPTR16 -#elif defined(DUK_OPT_NO_HEAPPTR16) -#undef DUK_USE_HEAPPTR16 -#else -#undef DUK_USE_HEAPPTR16 -#endif - -#if defined(DUK_OPT_HEAPPTR_DEC16) -#define DUK_USE_HEAPPTR_DEC16(udata,ptr) DUK_OPT_HEAPPTR_DEC16((udata),(ptr)) -#else -#undef DUK_USE_HEAPPTR_DEC16 -#endif - -#if defined(DUK_OPT_HEAPPTR_ENC16) -#define DUK_USE_HEAPPTR_ENC16(udata,ptr) DUK_OPT_HEAPPTR_ENC16((udata),(ptr)) -#else -#undef DUK_USE_HEAPPTR_ENC16 -#endif - -/* For now, hash part is dropped if and only if 16-bit object fields are used. */ -#define DUK_USE_HOBJECT_HASH_PART -#if defined(DUK_OPT_OBJSIZES16) -#undef DUK_USE_HOBJECT_HASH_PART -#endif - -#if defined(DUK_OPT_HSTRING_CLEN) -#define DUK_USE_HSTRING_CLEN -#elif defined(DUK_OPT_NO_HSTRING_CLEN) -#undef DUK_USE_HSTRING_CLEN -#else -#define DUK_USE_HSTRING_CLEN -#endif - -#if defined(DUK_OPT_EXTERNAL_STRINGS) -#define DUK_USE_HSTRING_EXTDATA -#elif defined(DUK_OPT_NO_EXTERNAL_STRINGS) -#undef DUK_USE_HSTRING_EXTDATA -#else -#undef DUK_USE_HSTRING_EXTDATA -#endif - -#if defined(DUK_OPT_INTERRUPT_COUNTER) -#define DUK_USE_INTERRUPT_COUNTER -#elif defined(DUK_OPT_NO_INTERRUPT_COUNTER) -#undef DUK_USE_INTERRUPT_COUNTER -#else -#undef DUK_USE_INTERRUPT_COUNTER -#endif - -#if defined(DUK_OPT_JC) -#define DUK_USE_JC -#elif defined(DUK_OPT_NO_JC) -#undef DUK_USE_JC -#else -#define DUK_USE_JC -#endif - -#if defined(DUK_OPT_JSON_STRINGIFY_FASTPATH) -#define DUK_USE_JSON_STRINGIFY_FASTPATH -#elif defined(DUK_OPT_NO_JSON_STRINGIFY_FASTPATH) -#undef DUK_USE_JSON_STRINGIFY_FASTPATH -#else -#undef DUK_USE_JSON_STRINGIFY_FASTPATH -#endif - -#if defined(DUK_OPT_JX) -#define DUK_USE_JX -#elif defined(DUK_OPT_NO_JX) -#undef DUK_USE_JX -#else -#define DUK_USE_JX -#endif - -#if defined(DUK_OPT_LIGHTFUNC_BUILTINS) -#define DUK_USE_LIGHTFUNC_BUILTINS -#elif defined(DUK_OPT_NO_LIGHTFUNC_BUILTINS) -#undef DUK_USE_LIGHTFUNC_BUILTINS -#else -#undef DUK_USE_LIGHTFUNC_BUILTINS -#endif - -#if defined(DUK_OPT_MARK_AND_SWEEP) -#define DUK_USE_MARK_AND_SWEEP -#elif defined(DUK_OPT_NO_MARK_AND_SWEEP) -#undef DUK_USE_MARK_AND_SWEEP -#else -#define DUK_USE_MARK_AND_SWEEP -#endif - -#if defined(DUK_OPT_MS_STRINGTABLE_RESIZE) -#define DUK_USE_MS_STRINGTABLE_RESIZE -#elif defined(DUK_OPT_NO_MS_STRINGTABLE_RESIZE) -#undef DUK_USE_MS_STRINGTABLE_RESIZE -#else -#define DUK_USE_MS_STRINGTABLE_RESIZE -#endif - -#if defined(DUK_OPT_NONSTD_ARRAY_CONCAT_TRAILER) -#define DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER -#elif defined(DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER) -#undef DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER -#else -#define DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER -#endif - -#if defined(DUK_OPT_NONSTD_ARRAY_MAP_TRAILER) -#define DUK_USE_NONSTD_ARRAY_MAP_TRAILER -#elif defined(DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER) -#undef DUK_USE_NONSTD_ARRAY_MAP_TRAILER -#else -#define DUK_USE_NONSTD_ARRAY_MAP_TRAILER -#endif - -#if defined(DUK_OPT_NONSTD_ARRAY_SPLICE_DELCOUNT) -#define DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT -#elif defined(DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT) -#undef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT -#else -#define DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT -#endif - -#if defined(DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY) -#define DUK_USE_NONSTD_FUNC_CALLER_PROPERTY -#elif defined(DUK_OPT_NO_NONSTD_FUNC_CALLER_PROPERTY) -#undef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY -#else -#undef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY -#endif - -#if defined(DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY) -#define DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY -#elif defined(DUK_OPT_NO_NONSTD_FUNC_SOURCE_PROPERTY) -#undef DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY -#else -#undef DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY -#endif - -#if defined(DUK_OPT_NONSTD_FUNC_STMT) -#define DUK_USE_NONSTD_FUNC_STMT -#elif defined(DUK_OPT_NO_NONSTD_FUNC_STMT) -#undef DUK_USE_NONSTD_FUNC_STMT -#else -#define DUK_USE_NONSTD_FUNC_STMT -#endif - -#if defined(DUK_OPT_NONSTD_ACCESSOR_KEY_ARGUMENT) -#define DUK_USE_NONSTD_GETTER_KEY_ARGUMENT -#elif defined(DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT) -#undef DUK_USE_NONSTD_GETTER_KEY_ARGUMENT -#else -#define DUK_USE_NONSTD_GETTER_KEY_ARGUMENT -#endif - -#if defined(DUK_OPT_NONSTD_JSON_ESC_U2028_U2029) -#define DUK_USE_NONSTD_JSON_ESC_U2028_U2029 -#elif defined(DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029) -#undef DUK_USE_NONSTD_JSON_ESC_U2028_U2029 -#else -#define DUK_USE_NONSTD_JSON_ESC_U2028_U2029 -#endif - -#if defined(DUK_OPT_NONSTD_REGEXP_DOLLAR_ESCAPE) -#define DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE -#elif defined(DUK_OPT_NO_NONSTD_REGEXP_DOLLAR_ESCAPE) -#undef DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE -#else -#define DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE -#endif - -#if defined(DUK_OPT_NONSTD_ACCESSOR_KEY_ARGUMENT) -#define DUK_USE_NONSTD_SETTER_KEY_ARGUMENT -#elif defined(DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT) -#undef DUK_USE_NONSTD_SETTER_KEY_ARGUMENT -#else -#define DUK_USE_NONSTD_SETTER_KEY_ARGUMENT -#endif - -#if defined(DUK_OPT_NONSTD_STRING_FROMCHARCODE_32BIT) -#define DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT -#elif defined(DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT) -#undef DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT -#else -#define DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT -#endif - -#if defined(DUK_OPT_OBJSIZES16) -#define DUK_USE_OBJSIZES16 -#elif defined(DUK_OPT_NO_OBJSIZES16) -#undef DUK_USE_OBJSIZES16 -#else -#undef DUK_USE_OBJSIZES16 -#endif - -#if defined(DUK_OPT_OCTAL_SUPPORT) -#define DUK_USE_OCTAL_SUPPORT -#elif defined(DUK_OPT_NO_OCTAL_SUPPORT) -#undef DUK_USE_OCTAL_SUPPORT -#else -#define DUK_USE_OCTAL_SUPPORT -#endif - -#if defined(DUK_OPT_PACKED_TVAL) -#define DUK_USE_PACKED_TVAL -#elif defined(DUK_OPT_NO_PACKED_TVAL) -#undef DUK_USE_PACKED_TVAL -#else -/* Already provided above */ -#endif - -#undef DUK_USE_PANIC_ABORT -#if !defined(DUK_OPT_SEGFAULT_ON_PANIC) -#define DUK_USE_PANIC_ABORT -#endif - -#undef DUK_USE_PANIC_HANDLER -#if defined(DUK_OPT_PANIC_HANDLER) -#define DUK_USE_PANIC_HANDLER(code,msg) DUK_OPT_PANIC_HANDLER((code),(msg)) -#endif - -#undef DUK_USE_PANIC_SEGFAULT -#if defined(DUK_OPT_SEGFAULT_ON_PANIC) -#define DUK_USE_PANIC_SEGFAULT -#endif - -#if defined(DUK_OPT_PARANOID_ERRORS) -#define DUK_USE_PARANOID_ERRORS -#elif defined(DUK_OPT_NO_PARANOID_ERRORS) -#undef DUK_USE_PARANOID_ERRORS -#else -#undef DUK_USE_PARANOID_ERRORS -#endif - -#if defined(DUK_OPT_PC2LINE) -#define DUK_USE_PC2LINE -#elif defined(DUK_OPT_NO_PC2LINE) -#undef DUK_USE_PC2LINE -#else -#define DUK_USE_PC2LINE -#endif - -#if defined(DUK_OPT_REFCOUNT16) -#define DUK_USE_REFCOUNT16 -#elif defined(DUK_OPT_NO_REFCOUNT16) -#undef DUK_USE_REFCOUNT16 -#else -#undef DUK_USE_REFCOUNT16 -#endif - -#if defined(DUK_OPT_REFERENCE_COUNTING) -#define DUK_USE_REFERENCE_COUNTING -#elif defined(DUK_OPT_NO_REFERENCE_COUNTING) -#undef DUK_USE_REFERENCE_COUNTING -#else -#define DUK_USE_REFERENCE_COUNTING -#endif - -#if defined(DUK_OPT_REGEXP_CANON_WORKAROUND) -#define DUK_USE_REGEXP_CANON_WORKAROUND -#elif defined(DUK_OPT_NO_REGEXP_CANON_WORKAROUND) -#undef DUK_USE_REGEXP_CANON_WORKAROUND -#else -#undef DUK_USE_REGEXP_CANON_WORKAROUND -#endif - -#if defined(DUK_OPT_REGEXP_SUPPORT) -#define DUK_USE_REGEXP_SUPPORT -#elif defined(DUK_OPT_NO_REGEXP_SUPPORT) -#undef DUK_USE_REGEXP_SUPPORT -#else -#define DUK_USE_REGEXP_SUPPORT -#endif - -#if defined(DUK_OPT_ROM_GLOBAL_CLONE) -#define DUK_USE_ROM_GLOBAL_CLONE -#elif defined(DUK_OPT_NO_ROM_GLOBAL_CLONE) -#undef DUK_USE_ROM_GLOBAL_CLONE -#else -#undef DUK_USE_ROM_GLOBAL_CLONE -#endif - -#if defined(DUK_OPT_ROM_GLOBAL_INHERIT) -#define DUK_USE_ROM_GLOBAL_INHERIT -#elif defined(DUK_OPT_NO_ROM_GLOBAL_INHERIT) -#undef DUK_USE_ROM_GLOBAL_INHERIT -#else -#undef DUK_USE_ROM_GLOBAL_INHERIT -#endif - -#if defined(DUK_OPT_ROM_OBJECTS) -#define DUK_USE_ROM_OBJECTS -#elif defined(DUK_OPT_NO_ROM_OBJECTS) -#undef DUK_USE_ROM_OBJECTS -#else -#undef DUK_USE_ROM_OBJECTS -#endif - -#if defined(DUK_OPT_ROM_STRINGS) -#define DUK_USE_ROM_STRINGS -#elif defined(DUK_OPT_NO_ROM_STRINGS) -#undef DUK_USE_ROM_STRINGS -#else -#undef DUK_USE_ROM_STRINGS -#endif - -#if defined(DUK_OPT_SECTION_B) -#define DUK_USE_SECTION_B -#elif defined(DUK_OPT_NO_SECTION_B) -#undef DUK_USE_SECTION_B -#else -#define DUK_USE_SECTION_B -#endif - -#if defined(DUK_OPT_SELF_TESTS) -#define DUK_USE_SELF_TESTS -#elif defined(DUK_OPT_NO_SELF_TESTS) -#undef DUK_USE_SELF_TESTS -#else -#undef DUK_USE_SELF_TESTS -#endif - -#if defined(DUK_OPT_SHUFFLE_TORTURE) -#define DUK_USE_SHUFFLE_TORTURE -#elif defined(DUK_OPT_NO_SHUFFLE_TORTURE) -#undef DUK_USE_SHUFFLE_TORTURE -#else -#undef DUK_USE_SHUFFLE_TORTURE -#endif - -#if defined(DUK_OPT_SOURCE_NONBMP) -#define DUK_USE_SOURCE_NONBMP -#elif defined(DUK_OPT_NO_SOURCE_NONBMP) -#undef DUK_USE_SOURCE_NONBMP -#else -#define DUK_USE_SOURCE_NONBMP -#endif - -#if defined(DUK_OPT_STRHASH16) -#define DUK_USE_STRHASH16 -#elif defined(DUK_OPT_NO_STRHASH16) -#undef DUK_USE_STRHASH16 -#else -#undef DUK_USE_STRHASH16 -#endif - -#if defined(DUK_OPT_STRICT_DECL) -#define DUK_USE_STRICT_DECL -#elif defined(DUK_OPT_NO_STRICT_DECL) -#undef DUK_USE_STRICT_DECL -#else -#define DUK_USE_STRICT_DECL -#endif - -#if defined(DUK_OPT_STRICT_UTF8_SOURCE) -#define DUK_USE_STRICT_UTF8_SOURCE -#elif defined(DUK_OPT_NO_STRICT_UTF8_SOURCE) -#undef DUK_USE_STRICT_UTF8_SOURCE -#else -#undef DUK_USE_STRICT_UTF8_SOURCE -#endif - -#if defined(DUK_OPT_STRLEN16) -#define DUK_USE_STRLEN16 -#elif defined(DUK_OPT_NO_STRLEN16) -#undef DUK_USE_STRLEN16 -#else -#undef DUK_USE_STRLEN16 -#endif - -#undef DUK_USE_STRTAB_CHAIN -#if defined(DUK_OPT_STRTAB_CHAIN) && defined(DUK_OPT_STRTAB_CHAIN_SIZE) -#define DUK_USE_STRTAB_CHAIN -#endif - -#undef DUK_USE_STRTAB_CHAIN_SIZE -#if defined(DUK_OPT_STRTAB_CHAIN) && defined(DUK_OPT_STRTAB_CHAIN_SIZE) -/* Low memory algorithm: separate chaining using arrays, fixed size hash */ -#define DUK_USE_STRTAB_CHAIN_SIZE DUK_OPT_STRTAB_CHAIN_SIZE -#endif - -#undef DUK_USE_STRTAB_PROBE -#if !(defined(DUK_OPT_STRTAB_CHAIN) && defined(DUK_OPT_STRTAB_CHAIN_SIZE)) -#define DUK_USE_STRTAB_PROBE -#endif - -#if defined(DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY) -#undef DUK_USE_TAILCALL -#else -#define DUK_USE_TAILCALL -#endif - -#if defined(DUK_OPT_TARGET_INFO) -#define DUK_USE_TARGET_INFO DUK_OPT_TARGET_INFO -#else -#define DUK_USE_TARGET_INFO "unknown" -#endif - -#if defined(DUK_OPT_NO_AUGMENT_ERRORS) -#undef DUK_USE_TRACEBACKS -#elif defined(DUK_OPT_NO_TRACEBACKS) -#undef DUK_USE_TRACEBACKS -#else -#define DUK_USE_TRACEBACKS -#endif - -#if defined(DUK_OPT_TRACEBACK_DEPTH) -#define DUK_USE_TRACEBACK_DEPTH DUK_OPT_TRACEBACK_DEPTH -#else -#define DUK_USE_TRACEBACK_DEPTH 10 -#endif - -#if defined(DUK_OPT_DECLARE) -#define DUK_USE_USER_DECLARE() DUK_OPT_DECLARE -#else -#define DUK_USE_USER_DECLARE() /* no user declarations */ -#endif - -/* User provided InitJS. */ -#undef DUK_USE_USER_INITJS -#if defined(DUK_OPT_USER_INITJS) -#define DUK_USE_USER_INITJS (DUK_OPT_USER_INITJS) -#endif - -#if defined(DUK_OPT_VERBOSE_ERRORS) -#define DUK_USE_VERBOSE_ERRORS -#elif defined(DUK_OPT_NO_VERBOSE_ERRORS) -#undef DUK_USE_VERBOSE_ERRORS -#else -#define DUK_USE_VERBOSE_ERRORS -#endif - -#if defined(DUK_OPT_VOLUNTARY_GC) -#define DUK_USE_VOLUNTARY_GC -#elif defined(DUK_OPT_NO_VOLUNTARY_GC) -#undef DUK_USE_VOLUNTARY_GC -#else -#define DUK_USE_VOLUNTARY_GC -#endif - -#if defined(DUK_OPT_ZERO_BUFFER_DATA) -#define DUK_USE_ZERO_BUFFER_DATA -#elif defined(DUK_OPT_NO_ZERO_BUFFER_DATA) -#undef DUK_USE_ZERO_BUFFER_DATA -#else -#define DUK_USE_ZERO_BUFFER_DATA -#endif - /* * Autogenerated defaults */ +#undef DUK_USE_ALLOW_UNDEFINED_BEHAVIOR +#define DUK_USE_ARRAY_BUILTIN +#define DUK_USE_ARRAY_FASTPATH +#define DUK_USE_ARRAY_PROP_FASTPATH +#undef DUK_USE_ASSERTIONS +#define DUK_USE_AUGMENT_ERROR_CREATE +#define DUK_USE_AUGMENT_ERROR_THROW #define DUK_USE_AVOID_PLATFORM_FUNCPTRS #define DUK_USE_BASE64_FASTPATH -#define DUK_USE_BUILTIN_INITJS +#define DUK_USE_BASE64_SUPPORT +#define DUK_USE_BOOLEAN_BUILTIN +#define DUK_USE_BUFFEROBJECT_SUPPORT +#undef DUK_USE_BUFLEN16 +#define DUK_USE_BYTECODE_DUMP_SUPPORT +#define DUK_USE_CACHE_ACTIVATION +#define DUK_USE_CACHE_CATCHER +#define DUK_USE_CALLSTACK_LIMIT 10000 +#define DUK_USE_CBOR_BUILTIN +#define DUK_USE_CBOR_DEC_RECLIMIT 1000 +#define DUK_USE_CBOR_ENC_RECLIMIT 1000 +#define DUK_USE_CBOR_SUPPORT #define DUK_USE_COMPILER_RECLIMIT 2500 +#define DUK_USE_COROUTINE_SUPPORT +#undef DUK_USE_CPP_EXCEPTIONS +#undef DUK_USE_DATAPTR16 +#undef DUK_USE_DATAPTR_DEC16 +#undef DUK_USE_DATAPTR_ENC16 +#define DUK_USE_DATE_BUILTIN #undef DUK_USE_DATE_FORMAT_STRING #undef DUK_USE_DATE_GET_LOCAL_TZOFFSET #undef DUK_USE_DATE_GET_NOW #undef DUK_USE_DATE_PARSE_STRING #undef DUK_USE_DATE_PRS_GETDATE +#undef DUK_USE_DEBUG +#undef DUK_USE_DEBUGGER_DUMPHEAP +#undef DUK_USE_DEBUGGER_INSPECT +#undef DUK_USE_DEBUGGER_PAUSE_UNCAUGHT +#undef DUK_USE_DEBUGGER_SUPPORT +#define DUK_USE_DEBUGGER_THROW_NOTIFY +#undef DUK_USE_DEBUGGER_TRANSPORT_TORTURE +#define DUK_USE_DEBUG_BUFSIZE 65536L +#define DUK_USE_DEBUG_LEVEL 0 +#undef DUK_USE_DEBUG_WRITE +#define DUK_USE_DOUBLE_LINKED_HEAP +#define DUK_USE_DUKTAPE_BUILTIN +#define DUK_USE_ENCODING_BUILTINS +#define DUK_USE_ERRCREATE +#define DUK_USE_ERRTHROW +#define DUK_USE_ES6 +#define DUK_USE_ES6_OBJECT_PROTO_PROPERTY +#define DUK_USE_ES6_OBJECT_SETPROTOTYPEOF +#define DUK_USE_ES6_PROXY +#define DUK_USE_ES6_REGEXP_SYNTAX +#define DUK_USE_ES6_UNICODE_ESCAPE +#define DUK_USE_ES7 +#define DUK_USE_ES7_EXP_OPERATOR +#define DUK_USE_ES8 +#define DUK_USE_ES9 #define DUK_USE_ESBC_LIMITS #define DUK_USE_ESBC_MAX_BYTES 2147418112L #define DUK_USE_ESBC_MAX_LINENUMBER 2147418112L #undef DUK_USE_EXEC_FUN_LOCAL +#undef DUK_USE_EXEC_INDIRECT_BOUND_CHECK +#undef DUK_USE_EXEC_PREFER_SIZE +#define DUK_USE_EXEC_REGCONST_OPTIMIZE +#undef DUK_USE_EXEC_TIMEOUT_CHECK #undef DUK_USE_EXPLICIT_NULL_INIT +#undef DUK_USE_EXTSTR_FREE +#undef DUK_USE_EXTSTR_INTERN_CHECK +#undef DUK_USE_FASTINT #define DUK_USE_FAST_REFCOUNT_DEFAULT +#undef DUK_USE_FATAL_HANDLER +#define DUK_USE_FATAL_MAXLEN 128 +#define DUK_USE_FINALIZER_SUPPORT +#undef DUK_USE_FINALIZER_TORTURE +#undef DUK_USE_FUNCPTR16 +#undef DUK_USE_FUNCPTR_DEC16 +#undef DUK_USE_FUNCPTR_ENC16 +#define DUK_USE_FUNCTION_BUILTIN +#define DUK_USE_FUNC_FILENAME_PROPERTY +#define DUK_USE_FUNC_NAME_PROPERTY +#undef DUK_USE_GC_TORTURE +#undef DUK_USE_GET_MONOTONIC_TIME +#undef DUK_USE_GET_RANDOM_DOUBLE +#define DUK_USE_GLOBAL_BINDING +#define DUK_USE_GLOBAL_BUILTIN +#undef DUK_USE_HEAPPTR16 +#undef DUK_USE_HEAPPTR_DEC16 +#undef DUK_USE_HEAPPTR_ENC16 #define DUK_USE_HEX_FASTPATH +#define DUK_USE_HEX_SUPPORT +#define DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT 2 +#define DUK_USE_HOBJECT_ARRAY_ABANDON_MINSIZE 257 +#define DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT 9 +#define DUK_USE_HOBJECT_ARRAY_MINGROW_ADD 16 +#define DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR 8 +#define DUK_USE_HOBJECT_ENTRY_MINGROW_ADD 16 +#define DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR 8 +#define DUK_USE_HOBJECT_HASH_PART +#define DUK_USE_HOBJECT_HASH_PROP_LIMIT 8 +#define DUK_USE_HSTRING_ARRIDX +#define DUK_USE_HSTRING_CLEN +#undef DUK_USE_HSTRING_EXTDATA +#define DUK_USE_HSTRING_LAZY_CLEN +#define DUK_USE_HTML_COMMENTS #define DUK_USE_IDCHAR_FASTPATH +#undef DUK_USE_INJECT_HEAP_ALLOC_ERROR +#undef DUK_USE_INTERRUPT_COUNTER #undef DUK_USE_INTERRUPT_DEBUG_FIXUP +#define DUK_USE_JC +#define DUK_USE_JSON_BUILTIN #define DUK_USE_JSON_DECNUMBER_FASTPATH #define DUK_USE_JSON_DECSTRING_FASTPATH #define DUK_USE_JSON_DEC_RECLIMIT 1000 #define DUK_USE_JSON_EATWHITE_FASTPATH #define DUK_USE_JSON_ENC_RECLIMIT 1000 #define DUK_USE_JSON_QUOTESTRING_FASTPATH +#undef DUK_USE_JSON_STRINGIFY_FASTPATH +#define DUK_USE_JSON_SUPPORT +#define DUK_USE_JX #define DUK_USE_LEXER_SLIDING_WINDOW -#undef DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE +#undef DUK_USE_LIGHTFUNC_BUILTINS +#define DUK_USE_LITCACHE_SIZE 256 #define DUK_USE_MARK_AND_SWEEP_RECLIMIT 256 #define DUK_USE_MATH_BUILTIN #define DUK_USE_NATIVE_CALL_RECLIMIT 1000 -#undef DUK_USE_PANIC_EXIT +#undef DUK_USE_NATIVE_STACK_CHECK +#define DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT +#undef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY +#undef DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY +#define DUK_USE_NONSTD_FUNC_STMT +#define DUK_USE_NONSTD_GETTER_KEY_ARGUMENT +#define DUK_USE_NONSTD_JSON_ESC_U2028_U2029 +#define DUK_USE_NONSTD_SETTER_KEY_ARGUMENT +#define DUK_USE_NONSTD_STRING_FROMCHARCODE_32BIT +#define DUK_USE_NUMBER_BUILTIN +#define DUK_USE_OBJECT_BUILTIN +#undef DUK_USE_OBJSIZES16 +#undef DUK_USE_PARANOID_ERRORS +#define DUK_USE_PC2LINE +#define DUK_USE_PERFORMANCE_BUILTIN #undef DUK_USE_PREFER_SIZE +#undef DUK_USE_PROMISE_BUILTIN #define DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS -#undef DUK_USE_REFZERO_FINALIZER_TORTURE +#undef DUK_USE_REFCOUNT16 +#define DUK_USE_REFCOUNT32 +#define DUK_USE_REFERENCE_COUNTING +#define DUK_USE_REFLECT_BUILTIN +#define DUK_USE_REGEXP_CANON_BITMAP +#undef DUK_USE_REGEXP_CANON_WORKAROUND #define DUK_USE_REGEXP_COMPILER_RECLIMIT 10000 #define DUK_USE_REGEXP_EXECUTOR_RECLIMIT 10000 +#define DUK_USE_REGEXP_SUPPORT +#undef DUK_USE_ROM_GLOBAL_CLONE +#undef DUK_USE_ROM_GLOBAL_INHERIT +#undef DUK_USE_ROM_OBJECTS #define DUK_USE_ROM_PTRCOMP_FIRST 63488L +#undef DUK_USE_ROM_STRINGS +#define DUK_USE_SECTION_B +#undef DUK_USE_SELF_TESTS +#define DUK_USE_SHEBANG_COMMENTS +#undef DUK_USE_SHUFFLE_TORTURE +#define DUK_USE_SOURCE_NONBMP +#undef DUK_USE_STRHASH16 #undef DUK_USE_STRHASH_DENSE #define DUK_USE_STRHASH_SKIP_SHIFT 5 +#define DUK_USE_STRICT_DECL +#undef DUK_USE_STRICT_UTF8_SOURCE +#define DUK_USE_STRING_BUILTIN +#undef DUK_USE_STRLEN16 +#define DUK_USE_STRTAB_GROW_LIMIT 17 +#define DUK_USE_STRTAB_MAXSIZE 268435456L +#define DUK_USE_STRTAB_MINSIZE 1024 +#undef DUK_USE_STRTAB_PTRCOMP +#define DUK_USE_STRTAB_RESIZE_CHECK_MASK 255 +#define DUK_USE_STRTAB_SHRINK_LIMIT 6 +#undef DUK_USE_STRTAB_TORTURE +#define DUK_USE_SYMBOL_BUILTIN +#define DUK_USE_TAILCALL +#define DUK_USE_TARGET_INFO "unknown" +#define DUK_USE_TRACEBACKS +#define DUK_USE_TRACEBACK_DEPTH 10 +#define DUK_USE_VALSTACK_GROW_SHIFT 2 +#define DUK_USE_VALSTACK_LIMIT 1000000L +#define DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT 2 +#define DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT 4 #undef DUK_USE_VALSTACK_UNSAFE +#define DUK_USE_VERBOSE_ERRORS #define DUK_USE_VERBOSE_EXECUTOR_ERRORS - -/* - * Alternative customization header - * - * If you want to modify the final DUK_USE_xxx flags directly (without - * using the available DUK_OPT_xxx flags), define DUK_OPT_HAVE_CUSTOM_H - * and tweak the final flags there. - */ - -#if defined(DUK_OPT_HAVE_CUSTOM_H) -#include "duk_custom.h" -#endif +#define DUK_USE_VOLUNTARY_GC +#define DUK_USE_ZERO_BUFFER_DATA /* * You may add overriding #define/#undef directives below for @@ -3548,6 +3096,25 @@ typedef FILE duk_file; */ /* __OVERRIDE_DEFINES__ */ +#define DUK_USE_CPP_EXCEPTIONS +#define DUK_USE_INTERRUPT_COUNTER +#define DUK_USE_EXEC_TIMEOUT_CHECK(udata) DukTimeoutCheck(udata) +#ifdef DUK_F_CPP +extern "C" { +#endif +extern int DukTimeoutCheck(void*); +#ifdef DUK_F_CPP +} +#endif + +/* + * Conditional includes + */ + +#if defined(DUK_F_CPP) && defined(DUK_USE_CPP_EXCEPTIONS) +#include /* std::exception */ +#include /* std::runtime_error */ +#endif /* * Date provider selection @@ -3562,21 +3129,25 @@ typedef FILE duk_file; #if defined(DUK_USE_DATE_GET_NOW) /* External provider already defined. */ #elif defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_gettimeofday((ctx)) +#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_gettimeofday() #elif defined(DUK_USE_DATE_NOW_TIME) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_time((ctx)) +#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_time() #elif defined(DUK_USE_DATE_NOW_WINDOWS) -#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows((ctx)) +#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows() +#elif defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) +#define DUK_USE_DATE_GET_NOW(ctx) duk_bi_date_get_now_windows_subms() #else #error no provider for DUK_USE_DATE_GET_NOW() #endif #if defined(DUK_USE_DATE_GET_LOCAL_TZOFFSET) /* External provider already defined. */ -#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME) +#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) #define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_gmtime((d)) #elif defined(DUK_USE_DATE_TZO_WINDOWS) #define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows((d)) +#elif defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) +#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows_no_dst((d)) #else #error no provider for DUK_USE_DATE_GET_LOCAL_TZOFFSET() #endif @@ -3600,8 +3171,317 @@ typedef FILE duk_file; /* No provider for DUK_USE_DATE_FORMAT_STRING(), fall back to ISO 8601 only. */ #endif +#if defined(DUK_USE_GET_MONOTONIC_TIME) +/* External provider already defined. */ +#elif defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) +#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_clock_gettime() +#elif defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) +#define DUK_USE_GET_MONOTONIC_TIME(ctx) duk_bi_date_get_monotonic_time_windows_qpc() +#else +/* No provider for DUK_USE_GET_MONOTONIC_TIME(), fall back to DUK_USE_DATE_GET_NOW(). */ +#endif + #endif /* DUK_COMPILING_DUKTAPE */ +/* + * Checks for legacy feature options (DUK_OPT_xxx) + */ + +#if defined(DUK_OPT_ASSERTIONS) +#error unsupported legacy feature option DUK_OPT_ASSERTIONS used +#endif +#if defined(DUK_OPT_BUFFEROBJECT_SUPPORT) +#error unsupported legacy feature option DUK_OPT_BUFFEROBJECT_SUPPORT used +#endif +#if defined(DUK_OPT_BUFLEN16) +#error unsupported legacy feature option DUK_OPT_BUFLEN16 used +#endif +#if defined(DUK_OPT_DATAPTR16) +#error unsupported legacy feature option DUK_OPT_DATAPTR16 used +#endif +#if defined(DUK_OPT_DATAPTR_DEC16) +#error unsupported legacy feature option DUK_OPT_DATAPTR_DEC16 used +#endif +#if defined(DUK_OPT_DATAPTR_ENC16) +#error unsupported legacy feature option DUK_OPT_DATAPTR_ENC16 used +#endif +#if defined(DUK_OPT_DDDPRINT) +#error unsupported legacy feature option DUK_OPT_DDDPRINT used +#endif +#if defined(DUK_OPT_DDPRINT) +#error unsupported legacy feature option DUK_OPT_DDPRINT used +#endif +#if defined(DUK_OPT_DEBUG) +#error unsupported legacy feature option DUK_OPT_DEBUG used +#endif +#if defined(DUK_OPT_DEBUGGER_DUMPHEAP) +#error unsupported legacy feature option DUK_OPT_DEBUGGER_DUMPHEAP used +#endif +#if defined(DUK_OPT_DEBUGGER_FWD_LOGGING) +#error unsupported legacy feature option DUK_OPT_DEBUGGER_FWD_LOGGING used +#endif +#if defined(DUK_OPT_DEBUGGER_FWD_PRINTALERT) +#error unsupported legacy feature option DUK_OPT_DEBUGGER_FWD_PRINTALERT used +#endif +#if defined(DUK_OPT_DEBUGGER_SUPPORT) +#error unsupported legacy feature option DUK_OPT_DEBUGGER_SUPPORT used +#endif +#if defined(DUK_OPT_DEBUGGER_TRANSPORT_TORTURE) +#error unsupported legacy feature option DUK_OPT_DEBUGGER_TRANSPORT_TORTURE used +#endif +#if defined(DUK_OPT_DEBUG_BUFSIZE) +#error unsupported legacy feature option DUK_OPT_DEBUG_BUFSIZE used +#endif +#if defined(DUK_OPT_DECLARE) +#error unsupported legacy feature option DUK_OPT_DECLARE used +#endif +#if defined(DUK_OPT_DEEP_C_STACK) +#error unsupported legacy feature option DUK_OPT_DEEP_C_STACK used +#endif +#if defined(DUK_OPT_DLL_BUILD) +#error unsupported legacy feature option DUK_OPT_DLL_BUILD used +#endif +#if defined(DUK_OPT_DPRINT) +#error unsupported legacy feature option DUK_OPT_DPRINT used +#endif +#if defined(DUK_OPT_DPRINT_COLORS) +#error unsupported legacy feature option DUK_OPT_DPRINT_COLORS used +#endif +#if defined(DUK_OPT_DPRINT_RDTSC) +#error unsupported legacy feature option DUK_OPT_DPRINT_RDTSC used +#endif +#if defined(DUK_OPT_EXEC_TIMEOUT_CHECK) +#error unsupported legacy feature option DUK_OPT_EXEC_TIMEOUT_CHECK used +#endif +#if defined(DUK_OPT_EXTERNAL_STRINGS) +#error unsupported legacy feature option DUK_OPT_EXTERNAL_STRINGS used +#endif +#if defined(DUK_OPT_EXTSTR_FREE) +#error unsupported legacy feature option DUK_OPT_EXTSTR_FREE used +#endif +#if defined(DUK_OPT_EXTSTR_INTERN_CHECK) +#error unsupported legacy feature option DUK_OPT_EXTSTR_INTERN_CHECK used +#endif +#if defined(DUK_OPT_FASTINT) +#error unsupported legacy feature option DUK_OPT_FASTINT used +#endif +#if defined(DUK_OPT_FORCE_ALIGN) +#error unsupported legacy feature option DUK_OPT_FORCE_ALIGN used +#endif +#if defined(DUK_OPT_FORCE_BYTEORDER) +#error unsupported legacy feature option DUK_OPT_FORCE_BYTEORDER used +#endif +#if defined(DUK_OPT_FUNCPTR16) +#error unsupported legacy feature option DUK_OPT_FUNCPTR16 used +#endif +#if defined(DUK_OPT_FUNCPTR_DEC16) +#error unsupported legacy feature option DUK_OPT_FUNCPTR_DEC16 used +#endif +#if defined(DUK_OPT_FUNCPTR_ENC16) +#error unsupported legacy feature option DUK_OPT_FUNCPTR_ENC16 used +#endif +#if defined(DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY) +#error unsupported legacy feature option DUK_OPT_FUNC_NONSTD_CALLER_PROPERTY used +#endif +#if defined(DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY) +#error unsupported legacy feature option DUK_OPT_FUNC_NONSTD_SOURCE_PROPERTY used +#endif +#if defined(DUK_OPT_GC_TORTURE) +#error unsupported legacy feature option DUK_OPT_GC_TORTURE used +#endif +#if defined(DUK_OPT_HAVE_CUSTOM_H) +#error unsupported legacy feature option DUK_OPT_HAVE_CUSTOM_H used +#endif +#if defined(DUK_OPT_HEAPPTR16) +#error unsupported legacy feature option DUK_OPT_HEAPPTR16 used +#endif +#if defined(DUK_OPT_HEAPPTR_DEC16) +#error unsupported legacy feature option DUK_OPT_HEAPPTR_DEC16 used +#endif +#if defined(DUK_OPT_HEAPPTR_ENC16) +#error unsupported legacy feature option DUK_OPT_HEAPPTR_ENC16 used +#endif +#if defined(DUK_OPT_INTERRUPT_COUNTER) +#error unsupported legacy feature option DUK_OPT_INTERRUPT_COUNTER used +#endif +#if defined(DUK_OPT_JSON_STRINGIFY_FASTPATH) +#error unsupported legacy feature option DUK_OPT_JSON_STRINGIFY_FASTPATH used +#endif +#if defined(DUK_OPT_LIGHTFUNC_BUILTINS) +#error unsupported legacy feature option DUK_OPT_LIGHTFUNC_BUILTINS used +#endif +#if defined(DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY) +#error unsupported legacy feature option DUK_OPT_NONSTD_FUNC_CALLER_PROPERTY used +#endif +#if defined(DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY) +#error unsupported legacy feature option DUK_OPT_NONSTD_FUNC_SOURCE_PROPERTY used +#endif +#if defined(DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT) +#error unsupported legacy feature option DUK_OPT_NO_ARRAY_SPLICE_NONSTD_DELCOUNT used +#endif +#if defined(DUK_OPT_NO_AUGMENT_ERRORS) +#error unsupported legacy feature option DUK_OPT_NO_AUGMENT_ERRORS used +#endif +#if defined(DUK_OPT_NO_BROWSER_LIKE) +#error unsupported legacy feature option DUK_OPT_NO_BROWSER_LIKE used +#endif +#if defined(DUK_OPT_NO_BUFFEROBJECT_SUPPORT) +#error unsupported legacy feature option DUK_OPT_NO_BUFFEROBJECT_SUPPORT used +#endif +#if defined(DUK_OPT_NO_BYTECODE_DUMP_SUPPORT) +#error unsupported legacy feature option DUK_OPT_NO_BYTECODE_DUMP_SUPPORT used +#endif +#if defined(DUK_OPT_NO_COMMONJS_MODULES) +#error unsupported legacy feature option DUK_OPT_NO_COMMONJS_MODULES used +#endif +#if defined(DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY) +#error unsupported legacy feature option DUK_OPT_NO_ES6_OBJECT_PROTO_PROPERTY used +#endif +#if defined(DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF) +#error unsupported legacy feature option DUK_OPT_NO_ES6_OBJECT_SETPROTOTYPEOF used +#endif +#if defined(DUK_OPT_NO_ES6_PROXY) +#error unsupported legacy feature option DUK_OPT_NO_ES6_PROXY used +#endif +#if defined(DUK_OPT_NO_FILE_IO) +#error unsupported legacy feature option DUK_OPT_NO_FILE_IO used +#endif +#if defined(DUK_OPT_NO_FUNC_STMT) +#error unsupported legacy feature option DUK_OPT_NO_FUNC_STMT used +#endif +#if defined(DUK_OPT_NO_JC) +#error unsupported legacy feature option DUK_OPT_NO_JC used +#endif +#if defined(DUK_OPT_NO_JSONC) +#error unsupported legacy feature option DUK_OPT_NO_JSONC used +#endif +#if defined(DUK_OPT_NO_JSONX) +#error unsupported legacy feature option DUK_OPT_NO_JSONX used +#endif +#if defined(DUK_OPT_NO_JX) +#error unsupported legacy feature option DUK_OPT_NO_JX used +#endif +#if defined(DUK_OPT_NO_MARK_AND_SWEEP) +#error unsupported legacy feature option DUK_OPT_NO_MARK_AND_SWEEP used +#endif +#if defined(DUK_OPT_NO_MS_STRINGTABLE_RESIZE) +#error unsupported legacy feature option DUK_OPT_NO_MS_STRINGTABLE_RESIZE used +#endif +#if defined(DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ACCESSOR_KEY_ARGUMENT used +#endif +#if defined(DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_CONCAT_TRAILER used +#endif +#if defined(DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_MAP_TRAILER used +#endif +#if defined(DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_ARRAY_SPLICE_DELCOUNT used +#endif +#if defined(DUK_OPT_NO_NONSTD_FUNC_STMT) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_FUNC_STMT used +#endif +#if defined(DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_JSON_ESC_U2028_U2029 used +#endif +#if defined(DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT) +#error unsupported legacy feature option DUK_OPT_NO_NONSTD_STRING_FROMCHARCODE_32BIT used +#endif +#if defined(DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY) +#error unsupported legacy feature option DUK_OPT_NO_OBJECT_ES6_PROTO_PROPERTY used +#endif +#if defined(DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF) +#error unsupported legacy feature option DUK_OPT_NO_OBJECT_ES6_SETPROTOTYPEOF used +#endif +#if defined(DUK_OPT_NO_OCTAL_SUPPORT) +#error unsupported legacy feature option DUK_OPT_NO_OCTAL_SUPPORT used +#endif +#if defined(DUK_OPT_NO_PACKED_TVAL) +#error unsupported legacy feature option DUK_OPT_NO_PACKED_TVAL used +#endif +#if defined(DUK_OPT_NO_PC2LINE) +#error unsupported legacy feature option DUK_OPT_NO_PC2LINE used +#endif +#if defined(DUK_OPT_NO_REFERENCE_COUNTING) +#error unsupported legacy feature option DUK_OPT_NO_REFERENCE_COUNTING used +#endif +#if defined(DUK_OPT_NO_REGEXP_SUPPORT) +#error unsupported legacy feature option DUK_OPT_NO_REGEXP_SUPPORT used +#endif +#if defined(DUK_OPT_NO_SECTION_B) +#error unsupported legacy feature option DUK_OPT_NO_SECTION_B used +#endif +#if defined(DUK_OPT_NO_SOURCE_NONBMP) +#error unsupported legacy feature option DUK_OPT_NO_SOURCE_NONBMP used +#endif +#if defined(DUK_OPT_NO_STRICT_DECL) +#error unsupported legacy feature option DUK_OPT_NO_STRICT_DECL used +#endif +#if defined(DUK_OPT_NO_TRACEBACKS) +#error unsupported legacy feature option DUK_OPT_NO_TRACEBACKS used +#endif +#if defined(DUK_OPT_NO_VERBOSE_ERRORS) +#error unsupported legacy feature option DUK_OPT_NO_VERBOSE_ERRORS used +#endif +#if defined(DUK_OPT_NO_VOLUNTARY_GC) +#error unsupported legacy feature option DUK_OPT_NO_VOLUNTARY_GC used +#endif +#if defined(DUK_OPT_NO_ZERO_BUFFER_DATA) +#error unsupported legacy feature option DUK_OPT_NO_ZERO_BUFFER_DATA used +#endif +#if defined(DUK_OPT_OBJSIZES16) +#error unsupported legacy feature option DUK_OPT_OBJSIZES16 used +#endif +#if defined(DUK_OPT_PANIC_HANDLER) +#error unsupported legacy feature option DUK_OPT_PANIC_HANDLER used +#endif +#if defined(DUK_OPT_REFCOUNT16) +#error unsupported legacy feature option DUK_OPT_REFCOUNT16 used +#endif +#if defined(DUK_OPT_SEGFAULT_ON_PANIC) +#error unsupported legacy feature option DUK_OPT_SEGFAULT_ON_PANIC used +#endif +#if defined(DUK_OPT_SELF_TESTS) +#error unsupported legacy feature option DUK_OPT_SELF_TESTS used +#endif +#if defined(DUK_OPT_SETJMP) +#error unsupported legacy feature option DUK_OPT_SETJMP used +#endif +#if defined(DUK_OPT_SHUFFLE_TORTURE) +#error unsupported legacy feature option DUK_OPT_SHUFFLE_TORTURE used +#endif +#if defined(DUK_OPT_SIGSETJMP) +#error unsupported legacy feature option DUK_OPT_SIGSETJMP used +#endif +#if defined(DUK_OPT_STRHASH16) +#error unsupported legacy feature option DUK_OPT_STRHASH16 used +#endif +#if defined(DUK_OPT_STRICT_UTF8_SOURCE) +#error unsupported legacy feature option DUK_OPT_STRICT_UTF8_SOURCE used +#endif +#if defined(DUK_OPT_STRLEN16) +#error unsupported legacy feature option DUK_OPT_STRLEN16 used +#endif +#if defined(DUK_OPT_STRTAB_CHAIN) +#error unsupported legacy feature option DUK_OPT_STRTAB_CHAIN used +#endif +#if defined(DUK_OPT_STRTAB_CHAIN_SIZE) +#error unsupported legacy feature option DUK_OPT_STRTAB_CHAIN_SIZE used +#endif +#if defined(DUK_OPT_TARGET_INFO) +#error unsupported legacy feature option DUK_OPT_TARGET_INFO used +#endif +#if defined(DUK_OPT_TRACEBACK_DEPTH) +#error unsupported legacy feature option DUK_OPT_TRACEBACK_DEPTH used +#endif +#if defined(DUK_OPT_UNDERSCORE_SETJMP) +#error unsupported legacy feature option DUK_OPT_UNDERSCORE_SETJMP used +#endif +#if defined(DUK_OPT_USER_INITJS) +#error unsupported legacy feature option DUK_OPT_USER_INITJS used +#endif + /* * Checks for config option consistency (DUK_USE_xxx) */ @@ -3615,15 +3495,36 @@ typedef FILE duk_file; #if defined(DUK_USE_ALIGN_8) #error unsupported config option used (option has been removed): DUK_USE_ALIGN_8 #endif +#if defined(DUK_USE_BROWSER_LIKE) +#error unsupported config option used (option has been removed): DUK_USE_BROWSER_LIKE +#endif +#if defined(DUK_USE_BUILTIN_INITJS) +#error unsupported config option used (option has been removed): DUK_USE_BUILTIN_INITJS +#endif #if defined(DUK_USE_BYTEORDER_FORCED) #error unsupported config option used (option has been removed): DUK_USE_BYTEORDER_FORCED #endif +#if defined(DUK_USE_COMMONJS_MODULES) +#error unsupported config option used (option has been removed): DUK_USE_COMMONJS_MODULES +#endif #if defined(DUK_USE_DATAPTR_DEC16) && !defined(DUK_USE_DATAPTR16) #error config option DUK_USE_DATAPTR_DEC16 requires option DUK_USE_DATAPTR16 (which is missing) #endif #if defined(DUK_USE_DATAPTR_ENC16) && !defined(DUK_USE_DATAPTR16) #error config option DUK_USE_DATAPTR_ENC16 requires option DUK_USE_DATAPTR16 (which is missing) #endif +#if defined(DUK_USE_DDDPRINT) +#error unsupported config option used (option has been removed): DUK_USE_DDDPRINT +#endif +#if defined(DUK_USE_DDPRINT) +#error unsupported config option used (option has been removed): DUK_USE_DDPRINT +#endif +#if defined(DUK_USE_DEBUGGER_FWD_LOGGING) +#error unsupported config option used (option has been removed): DUK_USE_DEBUGGER_FWD_LOGGING +#endif +#if defined(DUK_USE_DEBUGGER_FWD_PRINTALERT) +#error unsupported config option used (option has been removed): DUK_USE_DEBUGGER_FWD_PRINTALERT +#endif #if defined(DUK_USE_DEBUGGER_SUPPORT) && !defined(DUK_USE_INTERRUPT_COUNTER) #error config option DUK_USE_DEBUGGER_SUPPORT requires option DUK_USE_INTERRUPT_COUNTER (which is missing) #endif @@ -3657,9 +3558,21 @@ typedef FILE duk_file; #if defined(DUK_USE_DOUBLE_ME) && defined(DUK_USE_DOUBLE_BE) #error config option DUK_USE_DOUBLE_ME conflicts with option DUK_USE_DOUBLE_BE (which is also defined) #endif +#if defined(DUK_USE_DPRINT) +#error unsupported config option used (option has been removed): DUK_USE_DPRINT +#endif #if defined(DUK_USE_DPRINT) && !defined(DUK_USE_DEBUG) #error config option DUK_USE_DPRINT requires option DUK_USE_DEBUG (which is missing) #endif +#if defined(DUK_USE_DPRINT_COLORS) +#error unsupported config option used (option has been removed): DUK_USE_DPRINT_COLORS +#endif +#if defined(DUK_USE_DPRINT_RDTSC) +#error unsupported config option used (option has been removed): DUK_USE_DPRINT_RDTSC +#endif +#if defined(DUK_USE_ES6_REGEXP_BRACES) +#error unsupported config option used (option has been removed): DUK_USE_ES6_REGEXP_BRACES +#endif #if defined(DUK_USE_ESBC_MAX_BYTES) && !defined(DUK_USE_ESBC_LIMITS) #error config option DUK_USE_ESBC_MAX_BYTES requires option DUK_USE_ESBC_LIMITS (which is missing) #endif @@ -3675,6 +3588,12 @@ typedef FILE duk_file; #if defined(DUK_USE_EXTSTR_INTERN_CHECK) && !defined(DUK_USE_HSTRING_EXTDATA) #error config option DUK_USE_EXTSTR_INTERN_CHECK requires option DUK_USE_HSTRING_EXTDATA (which is missing) #endif +#if defined(DUK_USE_FASTINT) && !defined(DUK_USE_64BIT_OPS) +#error config option DUK_USE_FASTINT requires option DUK_USE_64BIT_OPS (which is missing) +#endif +#if defined(DUK_USE_FILE_IO) +#error unsupported config option used (option has been removed): DUK_USE_FILE_IO +#endif #if defined(DUK_USE_FULL_TVAL) #error unsupported config option used (option has been removed): DUK_USE_FULL_TVAL #endif @@ -3723,15 +3642,63 @@ typedef FILE duk_file; #if defined(DUK_USE_INTEGER_ME) && defined(DUK_USE_INTEGER_BE) #error config option DUK_USE_INTEGER_ME conflicts with option DUK_USE_INTEGER_BE (which is also defined) #endif +#if defined(DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE) +#error unsupported config option used (option has been removed): DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE +#endif +#if defined(DUK_USE_MARK_AND_SWEEP) +#error unsupported config option used (option has been removed): DUK_USE_MARK_AND_SWEEP +#endif +#if defined(DUK_USE_MATH_FMAX) +#error unsupported config option used (option has been removed): DUK_USE_MATH_FMAX +#endif +#if defined(DUK_USE_MATH_FMIN) +#error unsupported config option used (option has been removed): DUK_USE_MATH_FMIN +#endif +#if defined(DUK_USE_MATH_ROUND) +#error unsupported config option used (option has been removed): DUK_USE_MATH_ROUND +#endif +#if defined(DUK_USE_MS_STRINGTABLE_RESIZE) +#error unsupported config option used (option has been removed): DUK_USE_MS_STRINGTABLE_RESIZE +#endif +#if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER) +#error unsupported config option used (option has been removed): DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER +#endif +#if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER) +#error unsupported config option used (option has been removed): DUK_USE_NONSTD_ARRAY_MAP_TRAILER +#endif +#if defined(DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE) +#error unsupported config option used (option has been removed): DUK_USE_NONSTD_REGEXP_DOLLAR_ESCAPE +#endif #if defined(DUK_USE_NO_DOUBLE_ALIASING_SELFTEST) #error unsupported config option used (option has been removed): DUK_USE_NO_DOUBLE_ALIASING_SELFTEST #endif +#if defined(DUK_USE_OCTAL_SUPPORT) +#error unsupported config option used (option has been removed): DUK_USE_OCTAL_SUPPORT +#endif #if defined(DUK_USE_PACKED_TVAL_POSSIBLE) #error unsupported config option used (option has been removed): DUK_USE_PACKED_TVAL_POSSIBLE #endif +#if defined(DUK_USE_PANIC_ABORT) +#error unsupported config option used (option has been removed): DUK_USE_PANIC_ABORT +#endif +#if defined(DUK_USE_PANIC_EXIT) +#error unsupported config option used (option has been removed): DUK_USE_PANIC_EXIT +#endif +#if defined(DUK_USE_PANIC_HANDLER) +#error unsupported config option used (option has been removed): DUK_USE_PANIC_HANDLER +#endif +#if defined(DUK_USE_PANIC_SEGFAULT) +#error unsupported config option used (option has been removed): DUK_USE_PANIC_SEGFAULT +#endif +#if defined(DUK_USE_POW_NETBSD_WORKAROUND) +#error unsupported config option used (option has been removed): DUK_USE_POW_NETBSD_WORKAROUND +#endif #if defined(DUK_USE_RDTSC) #error unsupported config option used (option has been removed): DUK_USE_RDTSC #endif +#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE) +#error unsupported config option used (option has been removed): DUK_USE_REFZERO_FINALIZER_TORTURE +#endif #if defined(DUK_USE_ROM_GLOBAL_CLONE) && !defined(DUK_USE_ROM_STRINGS) #error config option DUK_USE_ROM_GLOBAL_CLONE requires option DUK_USE_ROM_STRINGS (which is missing) #endif @@ -3762,9 +3729,21 @@ typedef FILE duk_file; #if defined(DUK_USE_SIGSETJMP) #error unsupported config option used (option has been removed): DUK_USE_SIGSETJMP #endif +#if defined(DUK_USE_STRTAB_CHAIN) +#error unsupported config option used (option has been removed): DUK_USE_STRTAB_CHAIN +#endif +#if defined(DUK_USE_STRTAB_CHAIN_SIZE) +#error unsupported config option used (option has been removed): DUK_USE_STRTAB_CHAIN_SIZE +#endif #if defined(DUK_USE_STRTAB_CHAIN_SIZE) && !defined(DUK_USE_STRTAB_CHAIN) #error config option DUK_USE_STRTAB_CHAIN_SIZE requires option DUK_USE_STRTAB_CHAIN (which is missing) #endif +#if defined(DUK_USE_STRTAB_PROBE) +#error unsupported config option used (option has been removed): DUK_USE_STRTAB_PROBE +#endif +#if defined(DUK_USE_STRTAB_PTRCOMP) && !defined(DUK_USE_HEAPPTR16) +#error config option DUK_USE_STRTAB_PTRCOMP requires option DUK_USE_HEAPPTR16 (which is missing) +#endif #if defined(DUK_USE_TAILCALL) && defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) #error config option DUK_USE_TAILCALL conflicts with option DUK_USE_NONSTD_FUNC_CALLER_PROPERTY (which is also defined) #endif @@ -3774,6 +3753,12 @@ typedef FILE duk_file; #if defined(DUK_USE_UNDERSCORE_SETJMP) #error unsupported config option used (option has been removed): DUK_USE_UNDERSCORE_SETJMP #endif +#if defined(DUK_USE_USER_DECLARE) +#error unsupported config option used (option has been removed): DUK_USE_USER_DECLARE +#endif +#if defined(DUK_USE_USER_INITJS) +#error unsupported config option used (option has been removed): DUK_USE_USER_INITJS +#endif #if defined(DUK_USE_CPP_EXCEPTIONS) && !defined(__cplusplus) #error DUK_USE_CPP_EXCEPTIONS enabled but not compiling with a C++ compiler diff --git a/Source/3rdParty/duktape/duk_module_duktape.cpp b/Source/3rdParty/duktape/duk_module_duktape.cpp new file mode 100644 index 000000000..e2032849a --- /dev/null +++ b/Source/3rdParty/duktape/duk_module_duktape.cpp @@ -0,0 +1,477 @@ +/* + * Duktape 1.x compatible module loading framework + */ + +#include "duktape.h" +#include "duk_module_duktape.h" + + /* (v)snprintf() is missing before MSVC 2015. Note that _(v)snprintf() does + * NOT NUL terminate on truncation, but that's OK here. + * http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 + */ +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + +#if 0 /* Enable manually */ +#define DUK__ASSERT(x) do { \ + if (!(x)) { \ + fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \ + fflush(stderr); \ + } \ + } while (0) +#define DUK__ASSERT_TOP(ctx,val) do { \ + DUK__ASSERT(duk_get_top((ctx)) == (val)); \ + } while (0) +#else +#define DUK__ASSERT(x) do { (void) (x); } while (0) +#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0) +#endif + +static void duk__resolve_module_id(duk_context* ctx, const char* req_id, const char* mod_id) { + duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT]; + duk_uint8_t* p; + duk_uint8_t* q; + duk_uint8_t* q_last; /* last component */ + duk_int_t int_rc; + + DUK__ASSERT(req_id != NULL); + /* mod_id may be NULL */ + + /* + * A few notes on the algorithm: + * + * - Terms are not allowed to begin with a period unless the term + * is either '.' or '..'. This simplifies implementation (and + * is within CommonJS modules specification). + * + * - There are few output bound checks here. This is on purpose: + * the resolution input is length checked and the output is never + * longer than the input. The resolved output is written directly + * over the input because it's never longer than the input at any + * point in the algorithm. + * + * - Non-ASCII characters are processed as individual bytes and + * need no special treatment. However, U+0000 terminates the + * algorithm; this is not an issue because U+0000 is not a + * desirable term character anyway. + */ + + /* + * Set up the resolution input which is the requested ID directly + * (if absolute or no current module path) or with current module + * ID prepended (if relative and current module path exists). + * + * Suppose current module is 'foo/bar' and relative path is './quux'. + * The 'bar' component must be replaced so the initial input here is + * 'foo/bar/.././quux'. + */ + + if (mod_id != NULL && req_id[0] == '.') { + int_rc = snprintf((char*)buf, sizeof(buf), "%s/../%s", mod_id, req_id); + } + else { + int_rc = snprintf((char*)buf, sizeof(buf), "%s", req_id); + } + if (int_rc >= (duk_int_t)sizeof(buf) || int_rc < 0) { + /* Potentially truncated, NUL not guaranteed in any case. + * The (int_rc < 0) case should not occur in practice. + */ + goto resolve_error; + } + DUK__ASSERT(strlen((const char*)buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */ + + /* + * Resolution loop. At the top of the loop we're expecting a valid + * term: '.', '..', or a non-empty identifier not starting with a period. + */ + + p = buf; + q = buf; + for (;;) { + duk_uint_fast8_t c; + + /* Here 'p' always points to the start of a term. + * + * We can also unconditionally reset q_last here: if this is + * the last (non-empty) term q_last will have the right value + * on loop exit. + */ + + DUK__ASSERT(p >= q); /* output is never longer than input during resolution */ + + q_last = q; + + c = *p++; + if (c == 0) { + goto resolve_error; + } + else if (c == '.') { + c = *p++; + if (c == '/') { + /* Term was '.' and is eaten entirely (including dup slashes). */ + goto eat_dup_slashes; + } + if (c == '.' && *p == '/') { + /* Term was '..', backtrack resolved name by one component. + * q[-1] = previous slash (or beyond start of buffer) + * q[-2] = last char of previous component (or beyond start of buffer) + */ + p++; /* eat (first) input slash */ + DUK__ASSERT(q >= buf); + if (q == buf) { + goto resolve_error; + } + DUK__ASSERT(*(q - 1) == '/'); + q--; /* Backtrack to last output slash (dups already eliminated). */ + for (;;) { + /* Backtrack to previous slash or start of buffer. */ + DUK__ASSERT(q >= buf); + if (q == buf) { + break; + } + if (*(q - 1) == '/') { + break; + } + q--; + } + goto eat_dup_slashes; + } + goto resolve_error; + } + else if (c == '/') { + /* e.g. require('/foo'), empty terms not allowed */ + goto resolve_error; + } + else { + for (;;) { + /* Copy term name until end or '/'. */ + *q++ = c; + c = *p++; + if (c == 0) { + /* This was the last term, and q_last was + * updated to match this term at loop top. + */ + goto loop_done; + } + else if (c == '/') { + *q++ = '/'; + break; + } + else { + /* write on next loop */ + } + } + } + + eat_dup_slashes: + for (;;) { + /* eat dup slashes */ + c = *p; + if (c != '/') { + break; + } + p++; + } + } +loop_done: + /* Output #1: resolved absolute name. */ + DUK__ASSERT(q >= buf); + duk_push_lstring(ctx, (const char*)buf, (size_t)(q - buf)); + + /* Output #2: last component name. */ + DUK__ASSERT(q >= q_last); + DUK__ASSERT(q_last >= buf); + duk_push_lstring(ctx, (const char*)q_last, (size_t)(q - q_last)); + return; + +resolve_error: + (void)duk_type_error(ctx, "cannot resolve module id: %s", (const char*)req_id); +} + +/* Stack indices for better readability. */ +#define DUK__IDX_REQUESTED_ID 0 /* module id requested */ +#define DUK__IDX_REQUIRE 1 /* current require() function */ +#define DUK__IDX_REQUIRE_ID 2 /* the base ID of the current require() function, resolution base */ +#define DUK__IDX_RESOLVED_ID 3 /* resolved, normalized absolute module ID */ +#define DUK__IDX_LASTCOMP 4 /* last component name in resolved path */ +#define DUK__IDX_DUKTAPE 5 /* Duktape object */ +#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */ +#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */ +#define DUK__IDX_FRESH_REQUIRE 8 /* new require() function for module, updated resolution base */ +#define DUK__IDX_EXPORTS 9 /* default exports table */ +#define DUK__IDX_MODULE 10 /* module object containing module.exports, etc */ + +static duk_ret_t duk__require(duk_context* ctx) { + const char* str_req_id; /* requested identifier */ + const char* str_mod_id; /* require.id of current module */ + duk_int_t pcall_rc; + + /* NOTE: we try to minimize code size by avoiding unnecessary pops, + * so the stack looks a bit cluttered in this function. DUK__ASSERT_TOP() + * assertions are used to ensure stack configuration is correct at each + * step. + */ + + /* + * Resolve module identifier into canonical absolute form. + */ + + str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID); + duk_push_current_function(ctx); + duk_get_prop_string(ctx, -1, "id"); + str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */ + duk__resolve_module_id(ctx, str_req_id, str_mod_id); + str_req_id = NULL; + str_mod_id = NULL; + + /* [ requested_id require require.id resolved_id last_comp ] */ + DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1); + + /* + * Cached module check. + * + * If module has been loaded or its loading has already begun without + * finishing, return the same cached value (module.exports). The + * value is registered when module load starts so that circular + * references can be supported to some extent. + */ + + duk_push_global_stash(ctx); + duk_get_prop_string(ctx, -1, "\xff" "module:Duktape"); + duk_remove(ctx, -2); /* Lookup stashed, original 'Duktape' object. */ + duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded"); /* Duktape.modLoaded */ + duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT); + DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1); + + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) { + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */ + duk_get_prop_string(ctx, -1, "exports"); /* return module.exports */ + return 1; + } + DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */ + + /* + * Module not loaded (and loading not started previously). + * + * Create a new require() function with 'id' set to resolved ID + * of module being loaded. Also create 'exports' and 'module' + * tables but don't register exports to the loaded table yet. + * We don't want to do that unless the user module search callbacks + * succeeds in finding the module. + */ + + /* Fresh require: require.id is left configurable (but not writable) + * so that is not easy to accidentally tweak it, but it can still be + * done with Object.defineProperty(). + * + * XXX: require.id could also be just made non-configurable, as there + * is no practical reason to touch it (at least from ECMAScript code). + */ + duk_push_c_function(ctx, duk__require, 1 /*nargs*/); + duk_push_string(ctx, "name"); + duk_push_string(ctx, "require"); + duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE); /* not writable, not enumerable, not configurable */ + duk_push_string(ctx, "id"); + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE); /* a fresh require() with require.id = resolved target module id */ + + /* Module table: + * - module.exports: initial exports table (may be replaced by user) + * - module.id is non-writable and non-configurable, as the CommonJS + * spec suggests this if possible + * - module.filename: not set, defaults to resolved ID if not explicitly + * set by modSearch() (note capitalization, not .fileName, matches Node.js) + * - module.name: not set, defaults to last component of resolved ID if + * not explicitly set by modSearch() + */ + duk_push_object(ctx); /* exports */ + duk_push_object(ctx); /* module */ + duk_push_string(ctx, "exports"); + duk_dup(ctx, DUK__IDX_EXPORTS); + duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); /* module.exports = exports */ + duk_push_string(ctx, "id"); + duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */ + duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE); /* module.id = resolved_id; not writable, not enumerable, not configurable */ + duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */ + DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 1); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */ + + /* Register the module table early to modLoaded[] so that we can + * support circular references even in modSearch(). If an error + * is thrown, we'll delete the reference. + */ + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_dup(ctx, DUK__IDX_MODULE); + duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */ + + /* + * Call user provided module search function and build the wrapped + * module source code (if necessary). The module search function + * can be used to implement pure Ecmacsript, pure C, and mixed + * ECMAScript/C modules. + * + * The module search function can operate on the exports table directly + * (e.g. DLL code can register values to it). It can also return a + * string which is interpreted as module source code (if a non-string + * is returned the module is assumed to be a pure C one). If a module + * cannot be found, an error must be thrown by the user callback. + * + * Because Duktape.modLoaded[] already contains the module being + * loaded, circular references for C modules should also work + * (although expected to be quite rare). + */ + + duk_push_string(ctx, "(function(require,exports,module){"); + + /* Duktape.modSearch(resolved_id, fresh_require, exports, module). */ + duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch"); /* Duktape.modSearch */ + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); + duk_dup(ctx, DUK__IDX_EXPORTS); + duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */ + pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */ + DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 3); + + if (pcall_rc != DUK_EXEC_SUCCESS) { + /* Delete entry in Duktape.modLoaded[] and rethrow. */ + goto delete_rethrow; + } + + /* If user callback did not return source code, module loading + * is finished (user callback initialized exports table directly). + */ + if (!duk_is_string(ctx, -1)) { + /* User callback did not return source code, so module loading + * is finished: just update modLoaded with final module.exports + * and we're done. + */ + goto return_exports; + } + + /* Finish the wrapped module source. Force module.filename as the + * function .fileName so it gets set for functions defined within a + * module. This also ensures loggers created within the module get + * the module ID (or overridden filename) as their default logger name. + * (Note capitalization: .filename matches Node.js while .fileName is + * used elsewhere in Duktape.) + */ + duk_push_string(ctx, "\n})"); /* Newline allows module last line to contain a // comment. */ + duk_concat(ctx, 3); + if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) { + /* module.filename for .fileName, default to resolved ID if + * not present. + */ + duk_pop(ctx); + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + } + pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL); + if (pcall_rc != DUK_EXEC_SUCCESS) { + goto delete_rethrow; + } + pcall_rc = duk_pcall(ctx, 0); /* -> eval'd function wrapper (not called yet) */ + if (pcall_rc != DUK_EXEC_SUCCESS) { + goto delete_rethrow; + } + + /* Module has now evaluated to a wrapped module function. Force its + * .name to match module.name (defaults to last component of resolved + * ID) so that it is shown in stack traces too. Note that we must not + * introduce an actual name binding into the function scope (which is + * usually the case with a named function) because it would affect the + * scope seen by the module and shadow accesses to globals of the same name. + * This is now done by compiling the function as anonymous and then forcing + * its .name without setting a "has name binding" flag. + */ + + duk_push_string(ctx, "name"); + if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) { + /* module.name for .name, default to last component if + * not present. + */ + duk_pop(ctx); + duk_dup(ctx, DUK__IDX_LASTCOMP); + } + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); + + /* + * Call the wrapped module function. + * + * Use a protected call so that we can update Duktape.modLoaded[resolved_id] + * even if the module throws an error. + */ + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */ + DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); + + duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */ + duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */ + duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports"); /* relookup exports from module.exports in case it was changed by modSearch */ + duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */ + DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 6); + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */ + + pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/); + if (pcall_rc != DUK_EXEC_SUCCESS) { + /* Module loading failed. Node.js will forget the module + * registration so that another require() will try to load + * the module again. Mimic that behavior. + */ + goto delete_rethrow; + } + + /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */ + DUK__ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); + + /* fall through */ + +return_exports: + duk_get_prop_string(ctx, DUK__IDX_MODULE, "exports"); + duk_compact(ctx, -1); /* compact the exports table */ + return 1; /* return module.exports */ + +delete_rethrow: + duk_dup(ctx, DUK__IDX_RESOLVED_ID); + duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */ + (void)duk_throw(ctx); /* rethrow original error */ + return 0; /* not reachable */ +} + +void duk_module_duktape_init(duk_context* ctx) { + /* Stash 'Duktape' in case it's modified. */ + duk_push_global_stash(ctx); + duk_get_global_string(ctx, "Duktape"); + duk_put_prop_string(ctx, -2, "\xff" "module:Duktape"); + duk_pop(ctx); + + /* Register `require` as a global function. */ + duk_eval_string(ctx, + "(function(req){" + "var D=Object.defineProperty;" + "D(req,'name',{value:'require'});" + "D(this,'require',{value:req,writable:true,configurable:true});" + "D(Duktape,'modLoaded',{value:Object.create(null),writable:true,configurable:true});" + "})"); + duk_push_c_function(ctx, duk__require, 1 /*nargs*/); + duk_call(ctx, 1); + duk_pop(ctx); +} + +#undef DUK__ASSERT +#undef DUK__ASSERT_TOP +#undef DUK__IDX_REQUESTED_ID +#undef DUK__IDX_REQUIRE +#undef DUK__IDX_REQUIRE_ID +#undef DUK__IDX_RESOLVED_ID +#undef DUK__IDX_LASTCOMP +#undef DUK__IDX_DUKTAPE +#undef DUK__IDX_MODLOADED +#undef DUK__IDX_UNDEFINED +#undef DUK__IDX_FRESH_REQUIRE +#undef DUK__IDX_EXPORTS +#undef DUK__IDX_MODULE \ No newline at end of file diff --git a/Source/3rdParty/duktape/duk_module_duktape.h b/Source/3rdParty/duktape/duk_module_duktape.h new file mode 100644 index 000000000..42219a49e --- /dev/null +++ b/Source/3rdParty/duktape/duk_module_duktape.h @@ -0,0 +1,22 @@ +#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED) +#define DUK_MODULE_DUKTAPE_H_INCLUDED + +#include "duktape.h" + +#if defined(__cplusplus) +extern "C" { +#endif + + /* Maximum length of CommonJS module identifier to resolve. Length includes + * both current module ID, requested (possibly relative) module ID, and a + * slash in between. + */ +#define DUK_COMMONJS_MODULE_ID_LIMIT 256 + + extern void duk_module_duktape_init(duk_context* ctx); + +#if defined(__cplusplus) +} +#endif /* end 'extern "C"' wrapper */ + +#endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */ \ No newline at end of file diff --git a/Source/3rdParty/duktape/duktape.c b/Source/3rdParty/duktape/duktape.cpp similarity index 55% rename from Source/3rdParty/duktape/duktape.c rename to Source/3rdParty/duktape/duktape.cpp index 83edfd1d6..8b5889896 100644 --- a/Source/3rdParty/duktape/duktape.c +++ b/Source/3rdParty/duktape/duktape.cpp @@ -1,8 +1,8 @@ /* - * Single source autogenerated distributable for Duktape 1.5.1. + * Single source autogenerated distributable for Duktape 2.6.0. * - * Git commit 2cc76e9ff1f64869e1146ad7317d8cbe33bbd27e (v1.5.1). - * Git branch HEAD. + * Git commit fffa346eff06a8764b02c31d4336f63a773a95c3 (v2.6.0). + * Git branch v2-maintenance. * * See Duktape AUTHORS.rst and LICENSE.txt for copyright and * licensing information. @@ -16,7 +16,7 @@ * * (http://opensource.org/licenses/MIT) * -* Copyright (c) 2013-2016 by Duktape authors (see AUTHORS.rst) +* Copyright (c) 2013-2019 by Duktape authors (see AUTHORS.rst) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + /* AUTHORS.rst */ /* * =============== @@ -69,6 +70,41 @@ * * Ren\u00e9 Hollander * * Julien Hamaide (https://github.com/crazyjul) * * Sebastian G\u00f6tte (https://github.com/jaseg) +* * Tomasz Magulski (https://github.com/magul) +* * \D. Bohdan (https://github.com/dbohdan) +* * Ond\u0159ej Jirman (https://github.com/megous) +* * Sa\u00fal Ibarra Corretg\u00e9 +* * Jeremy HU +* * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr) +* * Harold Brenes (https://github.com/harold-b) +* * Oliver Crow (https://github.com/ocrow) +* * Jakub Ch\u0142api\u0144ski (https://github.com/jchlapinski) +* * Brett Vickers (https://github.com/beevik) +* * Dominik Okwieka (https://github.com/okitec) +* * Remko Tron\u00e7on (https://el-tramo.be) +* * Romero Malaquias (rbsm@ic.ufal.br) +* * Michael Drake +* * Steven Don (https://github.com/shdon) +* * Simon Stone (https://github.com/sstone1) +* * \J. McC. (https://github.com/jmhmccr) +* * Jakub Nowakowski (https://github.com/jimvonmoon) +* * Tommy Nguyen (https://github.com/tn0502) +* * Fabrice Fontaine (https://github.com/ffontaine) +* * Christopher Hiller (https://github.com/boneskull) +* * Gonzalo Diethelm (https://github.com/gonzus) +* * Michal Kasperek (https://github.com/michalkas) +* * Andrew Janke (https://github.com/apjanke) +* * Steve Fan (https://github.com/stevefan1999) +* * Edward Betts (https://github.com/edwardbetts) +* * Ozhan Duz (https://github.com/webfolderio) +* * Akos Kiss (https://github.com/akosthekiss) +* * TheBrokenRail (https://github.com/TheBrokenRail) +* * Jesse Doyle (https://github.com/jessedoyle) +* * Gero Kuehn (https://github.com/dc6jgk) +* * James Swift (https://github.com/phraemer) +* * Luis de Bethencourt (https://github.com/luisbg) +* * Ian Whyman (https://github.com/v00d00) +* * Rick Sayre (https://github.com/whorfin) * * Other contributions * =================== @@ -106,12 +142,25 @@ * * Michael Drake (https://github.com/tlsa) * * https://github.com/chris-y * * Laurent Zubiaur (https://github.com/lzubiaur) -* * Ole Andr\u00e9 Vadla Ravn\u00e5s (https://github.com/oleavr) +* * Neil Kolban (https://github.com/nkolban) +* * Wilhelm Wanecek (https://github.com/wanecek) +* * Andrew Janke (https://github.com/apjanke) +* * Unamer (https://github.com/unamer) +* * Karl Dahlke (eklhad@gmail.com) * * If you are accidentally missing from this list, send me an e-mail * (``sami.vaarala@iki.fi``) and I'll fix the omission. */ -#line 1 "duk_internal.h" + +/* + * Replacements for missing platform functions. + * + * Unlike the originals, fpclassify() and signbit() replacements don't + * work on any floating point types, only doubles. The C typing here + * mimics the standard prototypes. + */ + +/* #include duk_internal.h */ /* * Top-level include file to be used for all (internal) source files. * @@ -119,7 +168,7 @@ * have not been designed to be individually included. */ -#ifndef DUK_INTERNAL_H_INCLUDED +#if !defined(DUK_INTERNAL_H_INCLUDED) #define DUK_INTERNAL_H_INCLUDED /* @@ -139,15 +188,6 @@ #define DUK_COMPILING_DUKTAPE #include "duktape.h" -/* - * User declarations, e.g. prototypes for user functions used by Duktape - * macros. Concretely, if DUK_USE_PANIC_HANDLER is used and the macro - * value calls a user function, it needs to be declared for Duktape - * compilation to avoid warnings. - */ - -DUK_USE_USER_DECLARE() - /* * Duktape includes (other than duk_features.h) * @@ -157,8 +197,473 @@ DUK_USE_USER_DECLARE() * dependencies. */ -#line 1 "duk_replacements.h" -#ifndef DUK_REPLACEMENTS_H_INCLUDED +/* #include duk_dblunion.h */ +/* + * Union to access IEEE double memory representation, indexes for double + * memory representation, and some macros for double manipulation. + * + * Also used by packed duk_tval. Use a union for bit manipulation to + * minimize aliasing issues in practice. The C99 standard does not + * guarantee that this should work, but it's a very widely supported + * practice for low level manipulation. + * + * IEEE double format summary: + * + * seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff + * A B C D E F G H + * + * s sign bit + * eee... exponent field + * fff... fraction + * + * See http://en.wikipedia.org/wiki/Double_precision_floating-point_format. + * + * NaNs are represented as exponent 0x7ff and mantissa != 0. The NaN is a + * signaling NaN when the highest bit of the mantissa is zero, and a quiet + * NaN when the highest bit is set. + * + * At least three memory layouts are relevant here: + * + * A B C D E F G H Big endian (e.g. 68k) DUK_USE_DOUBLE_BE + * H G F E D C B A Little endian (e.g. x86) DUK_USE_DOUBLE_LE + * D C B A H G F E Mixed endian (e.g. ARM FPA) DUK_USE_DOUBLE_ME + * + * Legacy ARM (FPA) is a special case: ARM double values are in mixed + * endian format while ARM duk_uint64_t values are in standard little endian + * format (H G F E D C B A). When a double is read as a duk_uint64_t + * from memory, the register will contain the (logical) value + * E F G H A B C D. This requires some special handling below. + * See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0056d/Bcfhgcgd.html. + * + * Indexes of various types (8-bit, 16-bit, 32-bit) in memory relative to + * the logical (big endian) order: + * + * byte order duk_uint8_t duk_uint16_t duk_uint32_t + * BE 01234567 0123 01 + * LE 76543210 3210 10 + * ME (ARM) 32107654 1032 01 + * + * Some processors may alter NaN values in a floating point load+store. + * For instance, on X86 a FLD + FSTP may convert a signaling NaN to a + * quiet one. This is catastrophic when NaN space is used in packed + * duk_tval values. See: misc/clang_aliasing.c. + */ + +#if !defined(DUK_DBLUNION_H_INCLUDED) +#define DUK_DBLUNION_H_INCLUDED + +/* + * Union for accessing double parts, also serves as packed duk_tval + */ + +union duk_double_union { + double d; + float f[2]; +#if defined(DUK_USE_64BIT_OPS) + duk_uint64_t ull[1]; +#endif + duk_uint32_t ui[2]; + duk_uint16_t us[4]; + duk_uint8_t uc[8]; +#if defined(DUK_USE_PACKED_TVAL) + void *vp[2]; /* used by packed duk_tval, assumes sizeof(void *) == 4 */ +#endif +}; + +typedef union duk_double_union duk_double_union; + +/* + * Indexes of various types with respect to big endian (logical) layout + */ + +#if defined(DUK_USE_DOUBLE_LE) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 +#endif +#define DUK_DBL_IDX_UI0 1 +#define DUK_DBL_IDX_UI1 0 +#define DUK_DBL_IDX_US0 3 +#define DUK_DBL_IDX_US1 2 +#define DUK_DBL_IDX_US2 1 +#define DUK_DBL_IDX_US3 0 +#define DUK_DBL_IDX_UC0 7 +#define DUK_DBL_IDX_UC1 6 +#define DUK_DBL_IDX_UC2 5 +#define DUK_DBL_IDX_UC3 4 +#define DUK_DBL_IDX_UC4 3 +#define DUK_DBL_IDX_UC5 2 +#define DUK_DBL_IDX_UC6 1 +#define DUK_DBL_IDX_UC7 0 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#elif defined(DUK_USE_DOUBLE_BE) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 +#endif +#define DUK_DBL_IDX_UI0 0 +#define DUK_DBL_IDX_UI1 1 +#define DUK_DBL_IDX_US0 0 +#define DUK_DBL_IDX_US1 1 +#define DUK_DBL_IDX_US2 2 +#define DUK_DBL_IDX_US3 3 +#define DUK_DBL_IDX_UC0 0 +#define DUK_DBL_IDX_UC1 1 +#define DUK_DBL_IDX_UC2 2 +#define DUK_DBL_IDX_UC3 3 +#define DUK_DBL_IDX_UC4 4 +#define DUK_DBL_IDX_UC5 5 +#define DUK_DBL_IDX_UC6 6 +#define DUK_DBL_IDX_UC7 7 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#elif defined(DUK_USE_DOUBLE_ME) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 /* not directly applicable, byte order differs from a double */ +#endif +#define DUK_DBL_IDX_UI0 0 +#define DUK_DBL_IDX_UI1 1 +#define DUK_DBL_IDX_US0 1 +#define DUK_DBL_IDX_US1 0 +#define DUK_DBL_IDX_US2 3 +#define DUK_DBL_IDX_US3 2 +#define DUK_DBL_IDX_UC0 3 +#define DUK_DBL_IDX_UC1 2 +#define DUK_DBL_IDX_UC2 1 +#define DUK_DBL_IDX_UC3 0 +#define DUK_DBL_IDX_UC4 7 +#define DUK_DBL_IDX_UC5 6 +#define DUK_DBL_IDX_UC6 5 +#define DUK_DBL_IDX_UC7 4 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#else +#error internal error +#endif + +/* + * Helper macros for reading/writing memory representation parts, used + * by duk_numconv.c and duk_tval.h. + */ + +#define DUK_DBLUNION_SET_DOUBLE(u,v) do { \ + (u)->d = (v); \ + } while (0) + +#define DUK_DBLUNION_SET_HIGH32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ + } while (0) + +#if defined(DUK_USE_64BIT_OPS) +#if defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ + } while (0) +#else +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = ((duk_uint64_t) (v)) << 32; \ + } while (0) +#endif +#else /* DUK_USE_64BIT_OPS */ +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0; \ + } while (0) +#endif /* DUK_USE_64BIT_OPS */ + +#define DUK_DBLUNION_SET_LOW32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ + } while (0) + +#define DUK_DBLUNION_GET_DOUBLE(u) ((u)->d) +#define DUK_DBLUNION_GET_HIGH32(u) ((u)->ui[DUK_DBL_IDX_UI0]) +#define DUK_DBLUNION_GET_LOW32(u) ((u)->ui[DUK_DBL_IDX_UI1]) + +#if defined(DUK_USE_64BIT_OPS) +#if defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_SET_UINT64(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) ((v) >> 32); \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ + } while (0) +#define DUK_DBLUNION_GET_UINT64(u) \ + ((((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI0]) << 32) | \ + ((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI1])) +#else +#define DUK_DBLUNION_SET_UINT64(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ + } while (0) +#define DUK_DBLUNION_GET_UINT64(u) ((u)->ull[DUK_DBL_IDX_ULL0]) +#endif +#define DUK_DBLUNION_SET_INT64(u,v) DUK_DBLUNION_SET_UINT64((u), (duk_uint64_t) (v)) +#define DUK_DBLUNION_GET_INT64(u) ((duk_int64_t) DUK_DBLUNION_GET_UINT64((u))) +#endif /* DUK_USE_64BIT_OPS */ + +/* + * Double NaN manipulation macros related to NaN normalization needed when + * using the packed duk_tval representation. NaN normalization is necessary + * to keep double values compatible with the duk_tval format. + * + * When packed duk_tval is used, the NaN space is used to store pointers + * and other tagged values in addition to NaNs. Actual NaNs are normalized + * to a specific quiet NaN. The macros below are used by the implementation + * to check and normalize NaN values when they might be created. The macros + * are essentially NOPs when the non-packed duk_tval representation is used. + * + * A FULL check is exact and checks all bits. A NOTFULL check is used by + * the packed duk_tval and works correctly for all NaNs except those that + * begin with 0x7ff0. Since the 'normalized NaN' values used with packed + * duk_tval begin with 0x7ff8, the partial check is reliable when packed + * duk_tval is used. The 0x7ff8 prefix means the normalized NaN will be a + * quiet NaN regardless of its remaining lower bits. + * + * The ME variant below is specifically for ARM byte order, which has the + * feature that while doubles have a mixed byte order (32107654), unsigned + * long long values has a little endian byte order (76543210). When writing + * a logical double value through a ULL pointer, the 32-bit words need to be + * swapped; hence the #if defined()s below for ULL writes with DUK_USE_DOUBLE_ME. + * This is not full ARM support but suffices for some environments. + */ + +#if defined(DUK_USE_64BIT_OPS) +#if defined(DUK_USE_DOUBLE_ME) +/* Macros for 64-bit ops + mixed endian doubles. */ +#define DUK__DBLUNION_SET_NAN_FULL(u) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x000000007ff80000); \ + } while (0) +#define DUK__DBLUNION_IS_NAN_FULL(u) \ + ((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000)) && \ + ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0xffffffff000fffff)) != 0)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff80000)) +#define DUK__DBLUNION_IS_ANYINF(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x000000007ff00000)) +#define DUK__DBLUNION_IS_POSINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff00000)) +#define DUK__DBLUNION_IS_NEGINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x00000000fff00000)) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x0000000000000000)) +#define DUK__DBLUNION_IS_POSZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000)) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000080000000)) +#else +/* Macros for 64-bit ops + big/little endian doubles. */ +#define DUK__DBLUNION_SET_NAN_FULL(u) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x7ff8000000000000); \ + } while (0) +#define DUK__DBLUNION_IS_NAN_FULL(u) \ + ((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000)) && \ + ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0x000fffffffffffff)) != 0)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff8000000000000)) +#define DUK__DBLUNION_IS_ANYINF(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x7ff0000000000000)) +#define DUK__DBLUNION_IS_POSINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff0000000000000)) +#define DUK__DBLUNION_IS_NEGINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0xfff0000000000000)) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x0000000000000000)) +#define DUK__DBLUNION_IS_POSZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000)) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x8000000000000000)) +#endif +#else /* DUK_USE_64BIT_OPS */ +/* Macros for no 64-bit ops, any endianness. */ +#define DUK__DBLUNION_SET_NAN_FULL(u) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) 0x7ff80000UL; \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0x00000000UL; \ + } while (0) +#define DUK__DBLUNION_IS_NAN_FULL(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL) && \ + (((u)->ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) != 0 || \ + (u)->ui[DUK_DBL_IDX_UI1] != 0)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff80000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_ANYINF(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x7ff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_POSINF(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_NEGINF(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_POSZERO(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#endif /* DUK_USE_64BIT_OPS */ + +#define DUK__DBLUNION_SET_NAN_NOTFULL(u) do { \ + (u)->us[DUK_DBL_IDX_US0] = 0x7ff8UL; \ + } while (0) + +#define DUK__DBLUNION_IS_NAN_NOTFULL(u) \ + /* E == 0x7ff, topmost four bits of F != 0 => assume NaN */ \ + ((((u)->us[DUK_DBL_IDX_US0] & 0x7ff0UL) == 0x7ff0UL) && \ + (((u)->us[DUK_DBL_IDX_US0] & 0x000fUL) != 0x0000UL)) + +#define DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL(u) \ + /* E == 0x7ff, F == 8 => normalized NaN */ \ + ((u)->us[DUK_DBL_IDX_US0] == 0x7ff8UL) + +#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL(u) do { \ + if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ + DUK__DBLUNION_SET_NAN_FULL((u)); \ + } \ + } while (0) + +#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL(u) do { \ + /* Check must be full. */ \ + if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ + DUK__DBLUNION_SET_NAN_NOTFULL((u)); \ + } \ + } while (0) + +/* Concrete macros for NaN handling used by the implementation internals. + * Chosen so that they match the duk_tval representation: with a packed + * duk_tval, ensure NaNs are properly normalized; with a non-packed duk_tval + * these are essentially NOPs. + */ + +#if defined(DUK_USE_PACKED_TVAL) +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL((u)) +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_FULL((u)) +#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_FULL((d)) +#if 0 +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL((u)) +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_NOTFULL((u)) +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL((u)) +#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_NOTFULL((d)) +#endif +#define DUK_DBLUNION_IS_NORMALIZED(u) \ + (!DUK_DBLUNION_IS_NAN((u)) || /* either not a NaN */ \ + DUK_DBLUNION_IS_NORMALIZED_NAN((u))) /* or is a normalized NaN */ +#else /* DUK_USE_PACKED_TVAL */ +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) /* nop: no need to normalize */ +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ +#define DUK_DBLUNION_IS_NORMALIZED(u) 1 /* all doubles are considered normalized */ +#define DUK_DBLUNION_SET_NAN(u) do { \ + /* in non-packed representation we don't care about which NaN is used */ \ + (u)->d = DUK_DOUBLE_NAN; \ + } while (0) +#endif /* DUK_USE_PACKED_TVAL */ + +#define DUK_DBLUNION_IS_ANYINF(u) DUK__DBLUNION_IS_ANYINF((u)) +#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u)) +#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u)) + +#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u)) +#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u)) +#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u)) + +/* XXX: native 64-bit byteswaps when available */ + +/* 64-bit byteswap, same operation independent of target endianness. */ +#define DUK_DBLUNION_BSWAP64(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp2; \ + (u)->ui[1] = duk__bswaptmp1; \ + } while (0) + +/* Byteswap an IEEE double in the duk_double_union from host to network + * order. For a big endian target this is a no-op. + */ +#if defined(DUK_USE_DOUBLE_LE) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp2; \ + (u)->ui[1] = duk__bswaptmp1; \ + } while (0) +#elif defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp1; \ + (u)->ui[1] = duk__bswaptmp2; \ + } while (0) +#elif defined(DUK_USE_DOUBLE_BE) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { } while (0) +#else +#error internal error, double endianness insane +#endif + +/* Reverse operation is the same. */ +#define DUK_DBLUNION_DOUBLE_NTOH(u) DUK_DBLUNION_DOUBLE_HTON((u)) + +/* Some sign bit helpers. */ +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000)) != 0) +#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] >> 63U)) +#else +#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] & 0x80000000UL) != 0) +#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] >> 31U)) +#endif + +#endif /* DUK_DBLUNION_H_INCLUDED */ +/* #include duk_fltunion.h */ +/* + * Union to access IEEE float memory representation. + */ + +#if !defined(DUK_FLTUNION_H_INCLUDED) +#define DUK_FLTUNION_H_INCLUDED + +/* #include duk_internal.h -> already included */ + +union duk_float_union { + float f; + duk_uint32_t ui[1]; + duk_uint16_t us[2]; + duk_uint8_t uc[4]; +}; + +typedef union duk_float_union duk_float_union; + +#if defined(DUK_USE_DOUBLE_LE) || defined(DUK_USE_DOUBLE_ME) +#define DUK_FLT_IDX_UI0 0 +#define DUK_FLT_IDX_US0 1 +#define DUK_FLT_IDX_US1 0 +#define DUK_FLT_IDX_UC0 3 +#define DUK_FLT_IDX_UC1 2 +#define DUK_FLT_IDX_UC2 1 +#define DUK_FLT_IDX_UC3 0 +#elif defined(DUK_USE_DOUBLE_BE) +#define DUK_FLT_IDX_UI0 0 +#define DUK_FLT_IDX_US0 0 +#define DUK_FLT_IDX_US1 1 +#define DUK_FLT_IDX_UC0 0 +#define DUK_FLT_IDX_UC1 1 +#define DUK_FLT_IDX_UC2 2 +#define DUK_FLT_IDX_UC3 3 +#else +#error internal error +#endif + +#endif /* DUK_FLTUNION_H_INCLUDED */ +/* #include duk_replacements.h */ +#if !defined(DUK_REPLACEMENTS_H_INCLUDED) #define DUK_REPLACEMENTS_H_INCLUDED #if !defined(DUK_SINGLE_FILE) @@ -168,6 +673,8 @@ DUK_INTERNAL_DECL double duk_computed_infinity; #if defined(DUK_USE_COMPUTED_NAN) DUK_INTERNAL_DECL double duk_computed_nan; #endif +#endif /* !DUK_SINGLE_FILE */ + #if defined(DUK_USE_REPL_FPCLASSIFY) DUK_INTERNAL_DECL int duk_repl_fpclassify(double x); #endif @@ -183,10 +690,9 @@ DUK_INTERNAL_DECL int duk_repl_isnan(double x); #if defined(DUK_USE_REPL_ISINF) DUK_INTERNAL_DECL int duk_repl_isinf(double x); #endif -#endif /* !DUK_SINGLE_FILE */ #endif /* DUK_REPLACEMENTS_H_INCLUDED */ -#line 1 "duk_jmpbuf.h" +/* #include duk_jmpbuf.h */ /* * Wrapper for jmp_buf. * @@ -197,7 +703,7 @@ DUK_INTERNAL_DECL int duk_repl_isinf(double x); * http://en.wikipedia.org/wiki/Setjmp.h#Member_types */ -#ifndef DUK_JMPBUF_H_INCLUDED +#if !defined(DUK_JMPBUF_H_INCLUDED) #define DUK_JMPBUF_H_INCLUDED #if defined(DUK_USE_CPP_EXCEPTIONS) @@ -211,31 +717,43 @@ struct duk_jmpbuf { #endif #endif /* DUK_JMPBUF_H_INCLUDED */ -#line 1 "duk_exception.h" +/* #include duk_exception.h */ /* - * Exception for Duktape internal throws when C++ exceptions are used + * Exceptions for Duktape internal throws when C++ exceptions are used * for long control transfers. - * - * Doesn't inherit from any exception base class to minimize the chance - * that user code would accidentally catch this exception. */ -#ifndef DUK_EXCEPTION_H_INCLUDED +#if !defined(DUK_EXCEPTION_H_INCLUDED) #define DUK_EXCEPTION_H_INCLUDED #if defined(DUK_USE_CPP_EXCEPTIONS) +/* Internal exception used as a setjmp-longjmp replacement. User code should + * NEVER see or catch this exception, so it doesn't inherit from any base + * class which should minimize the chance of user code accidentally catching + * the exception. + */ class duk_internal_exception { /* intentionally empty */ }; + +/* Fatal error, thrown as a specific C++ exception with C++ exceptions + * enabled. It is unsafe to continue; doing so may cause crashes or memory + * leaks. This is intended to be either uncaught, or caught by user code + * aware of the "unsafe to continue" semantics. + */ +class duk_fatal_exception : public virtual std::runtime_error { + public: + duk_fatal_exception(const char *message) : std::runtime_error(message) {} +}; #endif #endif /* DUK_EXCEPTION_H_INCLUDED */ -#line 1 "duk_forwdecl.h" +/* #include duk_forwdecl.h */ /* * Forward declarations for all Duktape structures. */ -#ifndef DUK_FORWDECL_H_INCLUDED +#if !defined(DUK_FORWDECL_H_INCLUDED) #define DUK_FORWDECL_H_INCLUDED /* @@ -251,13 +769,18 @@ struct duk_jmpbuf; /* duk_tval intentionally skipped */ struct duk_heaphdr; struct duk_heaphdr_string; +struct duk_harray; struct duk_hstring; struct duk_hstring_external; struct duk_hobject; -struct duk_hcompiledfunction; -struct duk_hnativefunction; +struct duk_hcompfunc; +struct duk_hnatfunc; +struct duk_hboundfunc; struct duk_hthread; -struct duk_hbufferobject; +struct duk_hbufobj; +struct duk_hdecenv; +struct duk_hobjenv; +struct duk_hproxy; struct duk_hbuffer; struct duk_hbuffer_fixed; struct duk_hbuffer_dynamic; @@ -272,11 +795,12 @@ struct duk_breakpoint; struct duk_activation; struct duk_catcher; -struct duk_strcache; struct duk_ljstate; +struct duk_strcache_entry; +struct duk_litcache_entry; struct duk_strtab_entry; -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) struct duk_fixedbuffer; #endif @@ -306,13 +830,18 @@ typedef struct duk_jmpbuf duk_jmpbuf; /* duk_tval intentionally skipped */ typedef struct duk_heaphdr duk_heaphdr; typedef struct duk_heaphdr_string duk_heaphdr_string; +typedef struct duk_harray duk_harray; typedef struct duk_hstring duk_hstring; typedef struct duk_hstring_external duk_hstring_external; typedef struct duk_hobject duk_hobject; -typedef struct duk_hcompiledfunction duk_hcompiledfunction; -typedef struct duk_hnativefunction duk_hnativefunction; -typedef struct duk_hbufferobject duk_hbufferobject; +typedef struct duk_hcompfunc duk_hcompfunc; +typedef struct duk_hnatfunc duk_hnatfunc; +typedef struct duk_hboundfunc duk_hboundfunc; typedef struct duk_hthread duk_hthread; +typedef struct duk_hbufobj duk_hbufobj; +typedef struct duk_hdecenv duk_hdecenv; +typedef struct duk_hobjenv duk_hobjenv; +typedef struct duk_hproxy duk_hproxy; typedef struct duk_hbuffer duk_hbuffer; typedef struct duk_hbuffer_fixed duk_hbuffer_fixed; typedef struct duk_hbuffer_dynamic duk_hbuffer_dynamic; @@ -327,11 +856,12 @@ typedef struct duk_breakpoint duk_breakpoint; typedef struct duk_activation duk_activation; typedef struct duk_catcher duk_catcher; -typedef struct duk_strcache duk_strcache; typedef struct duk_ljstate duk_ljstate; +typedef struct duk_strcache_entry duk_strcache_entry; +typedef struct duk_litcache_entry duk_litcache_entry; typedef struct duk_strtab_entry duk_strtab_entry; -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) typedef struct duk_fixedbuffer duk_fixedbuffer; #endif @@ -353,7 +883,7 @@ typedef struct duk_re_matcher_ctx duk_re_matcher_ctx; typedef struct duk_re_compiler_ctx duk_re_compiler_ctx; #endif /* DUK_FORWDECL_H_INCLUDED */ -#line 1 "duk_tval.h" +/* #include duk_tval.h */ /* * Tagged type definition (duk_tval) and accessor macros. * @@ -366,15 +896,13 @@ typedef struct duk_re_compiler_ctx duk_re_compiler_ctx; * 64-bit environments (it usually pads to 16 bytes per value). * * Selecting the tagged type format involves many trade-offs (memory - * use, size and performance of generated code, portability, etc), - * see doc/types.rst for a detailed discussion (especially of how the - * IEEE double format is used to pack tagged values). + * use, size and performance of generated code, portability, etc). * * NB: because macro arguments are often expressions, macros should * avoid evaluating their argument more than once. */ -#ifndef DUK_TVAL_H_INCLUDED +#if !defined(DUK_TVAL_H_INCLUDED) #define DUK_TVAL_H_INCLUDED /* sanity */ @@ -391,10 +919,17 @@ typedef struct duk_re_compiler_ctx duk_re_compiler_ctx; /* use duk_double_union as duk_tval directly */ typedef union duk_double_union duk_tval; +typedef struct { + duk_uint16_t a; + duk_uint16_t b; + duk_uint16_t c; + duk_uint16_t d; +} duk_tval_unused; /* tags */ #define DUK_TAG_NORMALIZED_NAN 0x7ff8UL /* the NaN variant we use */ /* avoid tag 0xfff0, no risk of confusion with negative infinity */ +#define DUK_TAG_MIN 0xfff1UL #if defined(DUK_USE_FASTINT) #define DUK_TAG_FASTINT 0xfff1UL /* embed: integer value */ #endif @@ -408,195 +943,226 @@ typedef union duk_double_union duk_tval; #define DUK_TAG_STRING 0xfff8UL /* embed: duk_hstring ptr */ #define DUK_TAG_OBJECT 0xfff9UL /* embed: duk_hobject ptr */ #define DUK_TAG_BUFFER 0xfffaUL /* embed: duk_hbuffer ptr */ +#define DUK_TAG_MAX 0xfffaUL /* for convenience */ #define DUK_XTAG_BOOLEAN_FALSE 0xfff50000UL #define DUK_XTAG_BOOLEAN_TRUE 0xfff50001UL +#define DUK_TVAL_IS_VALID_TAG(tv) \ + (DUK_TVAL_GET_TAG((tv)) - DUK_TAG_MIN <= DUK_TAG_MAX - DUK_TAG_MIN) + +/* DUK_TVAL_UNUSED initializer for duk_tval_unused, works for any endianness. */ +#define DUK_TVAL_UNUSED_INITIALIZER() \ + { DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED, DUK_TAG_UNUSED } + /* two casts to avoid gcc warning: "warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]" */ #if defined(DUK_USE_64BIT_OPS) #if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_TAGGEDPOINTER(v,h,tag) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 16) | (((duk_uint64_t) (duk_uint32_t) (h)) << 32); \ +#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 16) | (((duk_uint64_t) (duk_uint32_t) (h)) << 32); \ } while (0) #else -#define DUK__TVAL_SET_TAGGEDPOINTER(v,h,tag) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 48) | ((duk_uint64_t) (duk_uint32_t) (h)); \ +#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) (tag)) << 48) | ((duk_uint64_t) (duk_uint32_t) (h)); \ } while (0) #endif #else /* DUK_USE_64BIT_OPS */ -#define DUK__TVAL_SET_TAGGEDPOINTER(v,h,tag) do { \ - (v)->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) (tag)) << 16; \ - (v)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (h); \ +#define DUK__TVAL_SET_TAGGEDPOINTER(tv,h,tag) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) (tag)) << 16; \ + duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (h); \ } while (0) #endif /* DUK_USE_64BIT_OPS */ #if defined(DUK_USE_64BIT_OPS) /* Double casting for pointer to avoid gcc warning (cast from pointer to integer of different size) */ #if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 16) | \ - ((duk_uint64_t) (flags)) | \ - (((duk_uint64_t) (duk_uint32_t) (fp)) << 32); \ +#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 16) | \ + ((duk_uint64_t) (flags)) | \ + (((duk_uint64_t) (duk_uint32_t) (fp)) << 32); \ } while (0) #else -#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 48) | \ - (((duk_uint64_t) (flags)) << 32) | \ - ((duk_uint64_t) (duk_uint32_t) (fp)); \ +#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_LIGHTFUNC) << 48) | \ + (((duk_uint64_t) (flags)) << 32) | \ + ((duk_uint64_t) (duk_uint32_t) (fp)); \ } while (0) #endif #else /* DUK_USE_64BIT_OPS */ -#define DUK__TVAL_SET_LIGHTFUNC(v,fp,flags) do { \ - (v)->ui[DUK_DBL_IDX_UI0] = (((duk_uint32_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint32_t) (flags)); \ - (v)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (fp); \ +#define DUK__TVAL_SET_LIGHTFUNC(tv,fp,flags) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->ui[DUK_DBL_IDX_UI0] = (((duk_uint32_t) DUK_TAG_LIGHTFUNC) << 16) | ((duk_uint32_t) (flags)); \ + duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (fp); \ } while (0) #endif /* DUK_USE_64BIT_OPS */ #if defined(DUK_USE_FASTINT) /* Note: masking is done for 'i' to deal with negative numbers correctly */ #if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_SET_FASTINT(v,i) do { \ - (v)->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16 | (((duk_uint32_t) ((i) >> 32)) & 0x0000ffffUL); \ - (v)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ +#define DUK__TVAL_SET_I48(tv,i) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16 | (((duk_uint32_t) ((i) >> 32)) & 0x0000ffffUL); \ + duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ } while (0) -#define DUK__TVAL_SET_FASTINT_U32(v,i) do { \ - (v)->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16; \ - (v)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ +#define DUK__TVAL_SET_U32(tv,i) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->ui[DUK_DBL_IDX_UI0] = ((duk_uint32_t) DUK_TAG_FASTINT) << 16; \ + duk__tv->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (i); \ } while (0) #else -#define DUK__TVAL_SET_FASTINT(v,i) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & 0x0000ffffffffffffULL); \ +#define DUK__TVAL_SET_I48(tv,i) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & DUK_U64_CONSTANT(0x0000ffffffffffff)); \ } while (0) -#define DUK__TVAL_SET_FASTINT_U32(v,i) do { \ - (v)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (duk_uint64_t) (i); \ +#define DUK__TVAL_SET_U32(tv,i) do { \ + (tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (duk_uint64_t) (i); \ } while (0) #endif -#define DUK__TVAL_SET_FASTINT_I32(v,i) do { \ +/* This needs to go through a cast because sign extension is needed. */ +#define DUK__TVAL_SET_I32(tv,i) do { \ duk_int64_t duk__tmp = (duk_int64_t) (i); \ - DUK_TVAL_SET_FASTINT((v), duk__tmp); \ + DUK_TVAL_SET_I48((tv), duk__tmp); \ } while (0) -/* XXX: clumsy sign extend and masking of 16 topmost bits */ +/* XXX: Clumsy sign extend and masking of 16 topmost bits. */ #if defined(DUK_USE_DOUBLE_ME) -#define DUK__TVAL_GET_FASTINT(v) (((duk_int64_t) ((((duk_uint64_t) (v)->ui[DUK_DBL_IDX_UI0]) << 32) | ((duk_uint64_t) (v)->ui[DUK_DBL_IDX_UI1]))) << 16 >> 16) +#define DUK__TVAL_GET_FASTINT(tv) (((duk_int64_t) ((((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI0]) << 32) | ((duk_uint64_t) (tv)->ui[DUK_DBL_IDX_UI1]))) << 16 >> 16) #else -#define DUK__TVAL_GET_FASTINT(v) ((((duk_int64_t) (v)->ull[DUK_DBL_IDX_ULL0]) << 16) >> 16) +#define DUK__TVAL_GET_FASTINT(tv) ((((duk_int64_t) (tv)->ull[DUK_DBL_IDX_ULL0]) << 16) >> 16) #endif -#define DUK__TVAL_GET_FASTINT_U32(v) ((v)->ui[DUK_DBL_IDX_UI1]) -#define DUK__TVAL_GET_FASTINT_I32(v) ((duk_int32_t) (v)->ui[DUK_DBL_IDX_UI1]) +#define DUK__TVAL_GET_FASTINT_U32(tv) ((tv)->ui[DUK_DBL_IDX_UI1]) +#define DUK__TVAL_GET_FASTINT_I32(tv) ((duk_int32_t) (tv)->ui[DUK_DBL_IDX_UI1]) #endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_UNDEFINED(v) do { \ - (v)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNDEFINED; \ +#define DUK_TVAL_SET_UNDEFINED(tv) do { \ + (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNDEFINED; \ } while (0) -#define DUK_TVAL_SET_UNUSED(v) do { \ - (v)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNUSED; \ +#define DUK_TVAL_SET_UNUSED(tv) do { \ + (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_UNUSED; \ } while (0) -#define DUK_TVAL_SET_NULL(v) do { \ - (v)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_NULL; \ +#define DUK_TVAL_SET_NULL(tv) do { \ + (tv)->us[DUK_DBL_IDX_US0] = (duk_uint16_t) DUK_TAG_NULL; \ } while (0) -#define DUK_TVAL_SET_BOOLEAN(v,val) DUK_DBLUNION_SET_HIGH32((v), (((duk_uint32_t) DUK_TAG_BOOLEAN) << 16) | ((duk_uint32_t) (val))) +#define DUK_TVAL_SET_BOOLEAN(tv,val) DUK_DBLUNION_SET_HIGH32((tv), (((duk_uint32_t) DUK_TAG_BOOLEAN) << 16) | ((duk_uint32_t) (val))) -#define DUK_TVAL_SET_NAN(v) DUK_DBLUNION_SET_NAN_FULL((v)) +#define DUK_TVAL_SET_NAN(tv) DUK_DBLUNION_SET_NAN_FULL((tv)) /* Assumes that caller has normalized NaNs, otherwise trouble ahead. */ #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_DOUBLE(v,d) do { \ +#define DUK_TVAL_SET_DOUBLE(tv,d) do { \ duk_double_t duk__dblval; \ duk__dblval = (d); \ DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \ - DUK_DBLUNION_SET_DOUBLE((v), duk__dblval); \ + DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \ } while (0) -#define DUK_TVAL_SET_FASTINT(v,i) DUK__TVAL_SET_FASTINT((v), (i)) -#define DUK_TVAL_SET_FASTINT_I32(v,i) DUK__TVAL_SET_FASTINT_I32((v), (i)) -#define DUK_TVAL_SET_FASTINT_U32(v,i) DUK__TVAL_SET_FASTINT_U32((v), (i)) -#define DUK_TVAL_SET_NUMBER_CHKFAST(v,d) duk_tval_set_number_chkfast((v), (d)) -#define DUK_TVAL_SET_NUMBER(v,d) DUK_TVAL_SET_DOUBLE((v), (d)) -#define DUK_TVAL_CHKFAST_INPLACE(v) do { \ +#define DUK_TVAL_SET_I48(tv,i) DUK__TVAL_SET_I48((tv), (i)) +#define DUK_TVAL_SET_I32(tv,i) DUK__TVAL_SET_I32((tv), (i)) +#define DUK_TVAL_SET_U32(tv,i) DUK__TVAL_SET_U32((tv), (i)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d) duk_tval_set_number_chkfast_fast((tv), (d)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d) duk_tval_set_number_chkfast_slow((tv), (d)) +#define DUK_TVAL_SET_NUMBER(tv,d) DUK_TVAL_SET_DOUBLE((tv), (d)) +#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) do { \ duk_tval *duk__tv; \ duk_double_t duk__d; \ - duk__tv = (v); \ + duk__tv = (tv); \ if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST(duk__tv, duk__d); \ + DUK_TVAL_SET_NUMBER_CHKFAST_FAST(duk__tv, duk__d); \ } \ } while (0) -#else -#define DUK_TVAL_SET_DOUBLE(v,d) do { \ +#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) do { \ + duk_tval *duk__tv; \ + duk_double_t duk__d; \ + duk__tv = (tv); \ + if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ + duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ + DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(duk__tv, duk__d); \ + } \ + } while (0) +#else /* DUK_USE_FASTINT */ +#define DUK_TVAL_SET_DOUBLE(tv,d) do { \ duk_double_t duk__dblval; \ duk__dblval = (d); \ DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); \ - DUK_DBLUNION_SET_DOUBLE((v), duk__dblval); \ + DUK_DBLUNION_SET_DOUBLE((tv), duk__dblval); \ } while (0) -#define DUK_TVAL_SET_FASTINT(v,i) DUK_TVAL_SET_DOUBLE((v), (duk_double_t) (i)) /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_FASTINT_I32(v,i) DUK_TVAL_SET_DOUBLE((v), (duk_double_t) (i)) -#define DUK_TVAL_SET_FASTINT_U32(v,i) DUK_TVAL_SET_DOUBLE((v), (duk_double_t) (i)) -#define DUK_TVAL_SET_NUMBER_CHKFAST(v,d) DUK_TVAL_SET_DOUBLE((v), (d)) -#define DUK_TVAL_SET_NUMBER(v,d) DUK_TVAL_SET_DOUBLE((v), (d)) -#define DUK_TVAL_CHKFAST_INPLACE(v) do { } while (0) -#endif +#define DUK_TVAL_SET_I48(tv,i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) /* XXX: fast int-to-double */ +#define DUK_TVAL_SET_I32(tv,i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) +#define DUK_TVAL_SET_U32(tv,i) DUK_TVAL_SET_DOUBLE((tv), (duk_double_t) (i)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d) DUK_TVAL_SET_DOUBLE((tv), (d)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d) DUK_TVAL_SET_DOUBLE((tv), (d)) +#define DUK_TVAL_SET_NUMBER(tv,d) DUK_TVAL_SET_DOUBLE((tv), (d)) +#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) do { } while (0) +#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) do { } while (0) +#endif /* DUK_USE_FASTINT */ -#define DUK_TVAL_SET_LIGHTFUNC(v,fp,flags) DUK__TVAL_SET_LIGHTFUNC((v), (fp), (flags)) -#define DUK_TVAL_SET_STRING(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v), (h), DUK_TAG_STRING) -#define DUK_TVAL_SET_OBJECT(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v), (h), DUK_TAG_OBJECT) -#define DUK_TVAL_SET_BUFFER(v,h) DUK__TVAL_SET_TAGGEDPOINTER((v), (h), DUK_TAG_BUFFER) -#define DUK_TVAL_SET_POINTER(v,p) DUK__TVAL_SET_TAGGEDPOINTER((v), (p), DUK_TAG_POINTER) +#define DUK_TVAL_SET_FASTINT(tv,i) DUK_TVAL_SET_I48((tv), (i)) /* alias */ -#define DUK_TVAL_SET_TVAL(v,x) do { *(v) = *(x); } while (0) +#define DUK_TVAL_SET_LIGHTFUNC(tv,fp,flags) DUK__TVAL_SET_LIGHTFUNC((tv), (fp), (flags)) +#define DUK_TVAL_SET_STRING(tv,h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_STRING) +#define DUK_TVAL_SET_OBJECT(tv,h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_OBJECT) +#define DUK_TVAL_SET_BUFFER(tv,h) DUK__TVAL_SET_TAGGEDPOINTER((tv), (h), DUK_TAG_BUFFER) +#define DUK_TVAL_SET_POINTER(tv,p) DUK__TVAL_SET_TAGGEDPOINTER((tv), (p), DUK_TAG_POINTER) + +#define DUK_TVAL_SET_TVAL(tv,x) do { *(tv) = *(x); } while (0) /* getters */ -#define DUK_TVAL_GET_BOOLEAN(v) ((int) (v)->us[DUK_DBL_IDX_US1]) +#define DUK_TVAL_GET_BOOLEAN(tv) ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US1]) #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_GET_DOUBLE(v) ((v)->d) -#define DUK_TVAL_GET_FASTINT(v) DUK__TVAL_GET_FASTINT((v)) -#define DUK_TVAL_GET_FASTINT_U32(v) DUK__TVAL_GET_FASTINT_U32((v)) -#define DUK_TVAL_GET_FASTINT_I32(v) DUK__TVAL_GET_FASTINT_I32((v)) -#define DUK_TVAL_GET_NUMBER(v) duk_tval_get_number_packed((v)) +#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->d) +#define DUK_TVAL_GET_FASTINT(tv) DUK__TVAL_GET_FASTINT((tv)) +#define DUK_TVAL_GET_FASTINT_U32(tv) DUK__TVAL_GET_FASTINT_U32((tv)) +#define DUK_TVAL_GET_FASTINT_I32(tv) DUK__TVAL_GET_FASTINT_I32((tv)) +#define DUK_TVAL_GET_NUMBER(tv) duk_tval_get_number_packed((tv)) #else -#define DUK_TVAL_GET_NUMBER(v) ((v)->d) -#define DUK_TVAL_GET_DOUBLE(v) ((v)->d) +#define DUK_TVAL_GET_NUMBER(tv) ((tv)->d) +#define DUK_TVAL_GET_DOUBLE(tv) ((tv)->d) #endif -#define DUK_TVAL_GET_LIGHTFUNC(v,out_fp,out_flags) do { \ - (out_flags) = (v)->ui[DUK_DBL_IDX_UI0] & 0xffffUL; \ - (out_fp) = (duk_c_function) (v)->ui[DUK_DBL_IDX_UI1]; \ +#define DUK_TVAL_GET_LIGHTFUNC(tv,out_fp,out_flags) do { \ + (out_flags) = (tv)->ui[DUK_DBL_IDX_UI0] & 0xffffUL; \ + (out_fp) = (duk_c_function) (tv)->ui[DUK_DBL_IDX_UI1]; \ } while (0) -#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(v) ((duk_c_function) ((v)->ui[DUK_DBL_IDX_UI1])) -#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(v) (((int) (v)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL) -#define DUK_TVAL_GET_STRING(v) ((duk_hstring *) (v)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_OBJECT(v) ((duk_hobject *) (v)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_BUFFER(v) ((duk_hbuffer *) (v)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_POINTER(v) ((void *) (v)->vp[DUK_DBL_IDX_VP1]) -#define DUK_TVAL_GET_HEAPHDR(v) ((duk_heaphdr *) (v)->vp[DUK_DBL_IDX_VP1]) +#define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((duk_c_function) ((tv)->ui[DUK_DBL_IDX_UI1])) +#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) (((duk_small_uint_t) (tv)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL) +#define DUK_TVAL_GET_STRING(tv) ((duk_hstring *) (tv)->vp[DUK_DBL_IDX_VP1]) +#define DUK_TVAL_GET_OBJECT(tv) ((duk_hobject *) (tv)->vp[DUK_DBL_IDX_VP1]) +#define DUK_TVAL_GET_BUFFER(tv) ((duk_hbuffer *) (tv)->vp[DUK_DBL_IDX_VP1]) +#define DUK_TVAL_GET_POINTER(tv) ((void *) (tv)->vp[DUK_DBL_IDX_VP1]) +#define DUK_TVAL_GET_HEAPHDR(tv) ((duk_heaphdr *) (tv)->vp[DUK_DBL_IDX_VP1]) /* decoding */ -#define DUK_TVAL_GET_TAG(v) ((duk_small_uint_t) (v)->us[DUK_DBL_IDX_US0]) +#define DUK_TVAL_GET_TAG(tv) ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US0]) -#define DUK_TVAL_IS_UNDEFINED(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_UNDEFINED) -#define DUK_TVAL_IS_UNUSED(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_UNUSED) -#define DUK_TVAL_IS_NULL(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_NULL) -#define DUK_TVAL_IS_BOOLEAN(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_BOOLEAN) -#define DUK_TVAL_IS_BOOLEAN_TRUE(v) ((v)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_TRUE) -#define DUK_TVAL_IS_BOOLEAN_FALSE(v) ((v)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_FALSE) -#define DUK_TVAL_IS_LIGHTFUNC(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_LIGHTFUNC) -#define DUK_TVAL_IS_STRING(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_STRING) -#define DUK_TVAL_IS_OBJECT(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_OBJECT) -#define DUK_TVAL_IS_BUFFER(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_BUFFER) -#define DUK_TVAL_IS_POINTER(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_POINTER) +#define DUK_TVAL_IS_UNDEFINED(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNDEFINED) +#define DUK_TVAL_IS_UNUSED(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_UNUSED) +#define DUK_TVAL_IS_NULL(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_NULL) +#define DUK_TVAL_IS_BOOLEAN(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BOOLEAN) +#define DUK_TVAL_IS_BOOLEAN_TRUE(tv) ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_TRUE) +#define DUK_TVAL_IS_BOOLEAN_FALSE(tv) ((tv)->ui[DUK_DBL_IDX_UI0] == DUK_XTAG_BOOLEAN_FALSE) +#define DUK_TVAL_IS_LIGHTFUNC(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_LIGHTFUNC) +#define DUK_TVAL_IS_STRING(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_STRING) +#define DUK_TVAL_IS_OBJECT(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_OBJECT) +#define DUK_TVAL_IS_BUFFER(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_BUFFER) +#define DUK_TVAL_IS_POINTER(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_POINTER) #if defined(DUK_USE_FASTINT) /* 0xfff0 is -Infinity */ -#define DUK_TVAL_IS_DOUBLE(v) (DUK_TVAL_GET_TAG((v)) <= 0xfff0UL) -#define DUK_TVAL_IS_FASTINT(v) (DUK_TVAL_GET_TAG((v)) == DUK_TAG_FASTINT) -#define DUK_TVAL_IS_NUMBER(v) (DUK_TVAL_GET_TAG((v)) <= 0xfff1UL) +#define DUK_TVAL_IS_DOUBLE(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL) +#define DUK_TVAL_IS_FASTINT(tv) (DUK_TVAL_GET_TAG((tv)) == DUK_TAG_FASTINT) +#define DUK_TVAL_IS_NUMBER(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff1UL) #else -#define DUK_TVAL_IS_NUMBER(v) (DUK_TVAL_GET_TAG((v)) <= 0xfff0UL) -#define DUK_TVAL_IS_DOUBLE(v) DUK_TVAL_IS_NUMBER((v)) +#define DUK_TVAL_IS_NUMBER(tv) (DUK_TVAL_GET_TAG((tv)) <= 0xfff0UL) +#define DUK_TVAL_IS_DOUBLE(tv) DUK_TVAL_IS_NUMBER((tv)) #endif /* This is performance critical because it appears in every DECREF. */ -#define DUK_TVAL_IS_HEAP_ALLOCATED(v) (DUK_TVAL_GET_TAG((v)) >= DUK_TAG_STRING) +#define DUK_TVAL_IS_HEAP_ALLOCATED(tv) (DUK_TVAL_GET_TAG((tv)) >= DUK_TAG_STRING) #if defined(DUK_USE_FASTINT) DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_packed(duk_tval *tv); @@ -631,8 +1197,8 @@ struct duk_tval_struct { void *voidptr; duk_hstring *hstring; duk_hobject *hobject; - duk_hcompiledfunction *hcompiledfunction; - duk_hnativefunction *hnativefunction; + duk_hcompfunc *hcompfunc; + duk_hnatfunc *hnatfunc; duk_hthread *hthread; duk_hbuffer *hbuffer; duk_heaphdr *heaphdr; @@ -640,7 +1206,22 @@ struct duk_tval_struct { } v; }; -#define DUK__TAG_NUMBER 0 /* not exposed */ +typedef struct { + duk_small_uint_t t; + duk_small_uint_t v_extra; + /* The rest of the fields don't matter except for debug dumps and such + * for which a partial initializer may trigger out-ot-bounds memory + * reads. Include a double field which is usually as large or larger + * than pointers (not always however). + */ + duk_double_t d; +} duk_tval_unused; + +#define DUK_TVAL_UNUSED_INITIALIZER() \ + { DUK_TAG_UNUSED, 0, 0.0 } + +#define DUK_TAG_MIN 0 +#define DUK_TAG_NUMBER 0 /* DUK_TAG_NUMBER only defined for non-packed duk_tval */ #if defined(DUK_USE_FASTINT) #define DUK_TAG_FASTINT 1 #endif @@ -653,8 +1234,12 @@ struct duk_tval_struct { #define DUK_TAG_STRING 8 /* first heap allocated, match bit boundary */ #define DUK_TAG_OBJECT 9 #define DUK_TAG_BUFFER 10 +#define DUK_TAG_MAX 10 -/* DUK__TAG_NUMBER is intentionally first, as it is the default clause in code +#define DUK_TVAL_IS_VALID_TAG(tv) \ + (DUK_TVAL_GET_TAG((tv)) - DUK_TAG_MIN <= DUK_TAG_MAX - DUK_TAG_MIN) + +/* DUK_TAG_NUMBER is intentionally first, as it is the default clause in code * to support the 8-byte representation. Further, it is a non-heap-allocated * type so it should come before DUK_TAG_STRING. Finally, it should not break * the tag value ranges covered by case-clauses in a switch-case. @@ -662,106 +1247,159 @@ struct duk_tval_struct { /* setters */ #define DUK_TVAL_SET_UNDEFINED(tv) do { \ - (tv)->t = DUK_TAG_UNDEFINED; \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_UNDEFINED; \ } while (0) #define DUK_TVAL_SET_UNUSED(tv) do { \ - (tv)->t = DUK_TAG_UNUSED; \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_UNUSED; \ } while (0) #define DUK_TVAL_SET_NULL(tv) do { \ - (tv)->t = DUK_TAG_NULL; \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_NULL; \ } while (0) #define DUK_TVAL_SET_BOOLEAN(tv,val) do { \ - (tv)->t = DUK_TAG_BOOLEAN; \ - (tv)->v.i = (val); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_BOOLEAN; \ + duk__tv->v.i = (duk_small_int_t) (val); \ } while (0) #if defined(DUK_USE_FASTINT) #define DUK_TVAL_SET_DOUBLE(tv,val) do { \ - (tv)->t = DUK__TAG_NUMBER; \ - (tv)->v.d = (val); \ + duk_tval *duk__tv; \ + duk_double_t duk__dblval; \ + duk__dblval = (val); \ + DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); /* nop for unpacked duk_tval */ \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_NUMBER; \ + duk__tv->v.d = duk__dblval; \ } while (0) -#define DUK_TVAL_SET_FASTINT(tv,val) do { \ - (tv)->t = DUK_TAG_FASTINT; \ - (tv)->v.fi = (val); \ +#define DUK_TVAL_SET_I48(tv,val) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_FASTINT; \ + duk__tv->v.fi = (val); \ } while (0) -#define DUK_TVAL_SET_FASTINT_U32(tv,val) do { \ - (tv)->t = DUK_TAG_FASTINT; \ - (tv)->v.fi = (duk_int64_t) (val); \ +#define DUK_TVAL_SET_U32(tv,val) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_FASTINT; \ + duk__tv->v.fi = (duk_int64_t) (val); \ } while (0) -#define DUK_TVAL_SET_FASTINT_I32(tv,val) do { \ - (tv)->t = DUK_TAG_FASTINT; \ - (tv)->v.fi = (duk_int64_t) (val); \ +#define DUK_TVAL_SET_I32(tv,val) do { \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_FASTINT; \ + duk__tv->v.fi = (duk_int64_t) (val); \ } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST(tv,d) \ - duk_tval_set_number_chkfast((tv), (d)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d) \ + duk_tval_set_number_chkfast_fast((tv), (d)) +#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d) \ + duk_tval_set_number_chkfast_slow((tv), (d)) #define DUK_TVAL_SET_NUMBER(tv,val) \ DUK_TVAL_SET_DOUBLE((tv), (val)) -#define DUK_TVAL_CHKFAST_INPLACE(v) do { \ +#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) do { \ duk_tval *duk__tv; \ duk_double_t duk__d; \ - duk__tv = (v); \ + duk__tv = (tv); \ if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ - DUK_TVAL_SET_NUMBER_CHKFAST(duk__tv, duk__d); \ + DUK_TVAL_SET_NUMBER_CHKFAST_FAST(duk__tv, duk__d); \ } \ } while (0) -#else +#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) do { \ + duk_tval *duk__tv; \ + duk_double_t duk__d; \ + duk__tv = (tv); \ + if (DUK_TVAL_IS_DOUBLE(duk__tv)) { \ + duk__d = DUK_TVAL_GET_DOUBLE(duk__tv); \ + DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(duk__tv, duk__d); \ + } \ + } while (0) +#else /* DUK_USE_FASTINT */ #define DUK_TVAL_SET_DOUBLE(tv,d) \ DUK_TVAL_SET_NUMBER((tv), (d)) -#define DUK_TVAL_SET_FASTINT(tv,val) \ +#define DUK_TVAL_SET_I48(tv,val) \ DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_FASTINT_U32(tv,val) \ +#define DUK_TVAL_SET_U32(tv,val) \ DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) -#define DUK_TVAL_SET_FASTINT_I32(tv,val) \ +#define DUK_TVAL_SET_I32(tv,val) \ DUK_TVAL_SET_NUMBER((tv), (duk_double_t) (val)) #define DUK_TVAL_SET_NUMBER(tv,val) do { \ - (tv)->t = DUK__TAG_NUMBER; \ - (tv)->v.d = (val); \ + duk_tval *duk__tv; \ + duk_double_t duk__dblval; \ + duk__dblval = (val); \ + DUK_ASSERT_DOUBLE_IS_NORMALIZED(duk__dblval); /* nop for unpacked duk_tval */ \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_NUMBER; \ + duk__tv->v.d = duk__dblval; \ } while (0) -#define DUK_TVAL_SET_NUMBER_CHKFAST(tv,d) \ +#define DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv,d) \ DUK_TVAL_SET_NUMBER((tv), (d)) -#define DUK_TVAL_CHKFAST_INPLACE(v) do { } while (0) +#define DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv,d) \ + DUK_TVAL_SET_NUMBER((tv), (d)) +#define DUK_TVAL_CHKFAST_INPLACE_FAST(tv) do { } while (0) +#define DUK_TVAL_CHKFAST_INPLACE_SLOW(tv) do { } while (0) #endif /* DUK_USE_FASTINT */ +#define DUK_TVAL_SET_FASTINT(tv,i) \ + DUK_TVAL_SET_I48((tv), (i)) /* alias */ + #define DUK_TVAL_SET_POINTER(tv,hptr) do { \ - (tv)->t = DUK_TAG_POINTER; \ - (tv)->v.voidptr = (hptr); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_POINTER; \ + duk__tv->v.voidptr = (hptr); \ } while (0) #define DUK_TVAL_SET_LIGHTFUNC(tv,fp,flags) do { \ - (tv)->t = DUK_TAG_LIGHTFUNC; \ - (tv)->v_extra = (flags); \ - (tv)->v.lightfunc = (duk_c_function) (fp); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_LIGHTFUNC; \ + duk__tv->v_extra = (flags); \ + duk__tv->v.lightfunc = (duk_c_function) (fp); \ } while (0) #define DUK_TVAL_SET_STRING(tv,hptr) do { \ - (tv)->t = DUK_TAG_STRING; \ - (tv)->v.hstring = (hptr); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_STRING; \ + duk__tv->v.hstring = (hptr); \ } while (0) #define DUK_TVAL_SET_OBJECT(tv,hptr) do { \ - (tv)->t = DUK_TAG_OBJECT; \ - (tv)->v.hobject = (hptr); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_OBJECT; \ + duk__tv->v.hobject = (hptr); \ } while (0) #define DUK_TVAL_SET_BUFFER(tv,hptr) do { \ - (tv)->t = DUK_TAG_BUFFER; \ - (tv)->v.hbuffer = (hptr); \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_BUFFER; \ + duk__tv->v.hbuffer = (hptr); \ } while (0) #define DUK_TVAL_SET_NAN(tv) do { \ /* in non-packed representation we don't care about which NaN is used */ \ - (tv)->t = DUK__TAG_NUMBER; \ - (tv)->v.d = DUK_DOUBLE_NAN; \ + duk_tval *duk__tv; \ + duk__tv = (tv); \ + duk__tv->t = DUK_TAG_NUMBER; \ + duk__tv->v.d = DUK_DOUBLE_NAN; \ } while (0) -#define DUK_TVAL_SET_TVAL(v,x) do { *(v) = *(x); } while (0) +#define DUK_TVAL_SET_TVAL(tv,x) do { *(tv) = *(x); } while (0) /* getters */ -#define DUK_TVAL_GET_BOOLEAN(tv) ((tv)->v.i) +#define DUK_TVAL_GET_BOOLEAN(tv) ((duk_small_uint_t) (tv)->v.i) #if defined(DUK_USE_FASTINT) #define DUK_TVAL_GET_DOUBLE(tv) ((tv)->v.d) #define DUK_TVAL_GET_FASTINT(tv) ((tv)->v.fi) @@ -788,7 +1426,7 @@ struct duk_tval_struct { (out_fp) = (tv)->v.lightfunc; \ } while (0) #define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((tv)->v.lightfunc) -#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) ((duk_uint32_t) ((tv)->v_extra)) +#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv) ((duk_small_uint_t) ((tv)->v_extra)) #define DUK_TVAL_GET_STRING(tv) ((tv)->v.hstring) #define DUK_TVAL_GET_OBJECT(tv) ((tv)->v.hobject) #define DUK_TVAL_GET_BUFFER(tv) ((tv)->v.hbuffer) @@ -803,13 +1441,13 @@ struct duk_tval_struct { #define DUK_TVAL_IS_BOOLEAN_TRUE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i != 0)) #define DUK_TVAL_IS_BOOLEAN_FALSE(tv) (((tv)->t == DUK_TAG_BOOLEAN) && ((tv)->v.i == 0)) #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_IS_DOUBLE(tv) ((tv)->t == DUK__TAG_NUMBER) +#define DUK_TVAL_IS_DOUBLE(tv) ((tv)->t == DUK_TAG_NUMBER) #define DUK_TVAL_IS_FASTINT(tv) ((tv)->t == DUK_TAG_FASTINT) -#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK__TAG_NUMBER || \ +#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK_TAG_NUMBER || \ (tv)->t == DUK_TAG_FASTINT) #else -#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK__TAG_NUMBER) -#define DUK_TVAL_IS_DOUBLE(v) DUK_TVAL_IS_NUMBER((v)) +#define DUK_TVAL_IS_NUMBER(tv) ((tv)->t == DUK_TAG_NUMBER) +#define DUK_TVAL_IS_DOUBLE(tv) DUK_TVAL_IS_NUMBER((tv)) #endif /* DUK_USE_FASTINT */ #define DUK_TVAL_IS_POINTER(tv) ((tv)->t == DUK_TAG_POINTER) #define DUK_TVAL_IS_LIGHTFUNC(tv) ((tv)->t == DUK_TAG_LIGHTFUNC) @@ -840,19 +1478,24 @@ DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv * Convenience (independent of representation) */ -#define DUK_TVAL_SET_BOOLEAN_TRUE(v) DUK_TVAL_SET_BOOLEAN(v, 1) -#define DUK_TVAL_SET_BOOLEAN_FALSE(v) DUK_TVAL_SET_BOOLEAN(v, 0) +#define DUK_TVAL_SET_BOOLEAN_TRUE(tv) DUK_TVAL_SET_BOOLEAN((tv), 1) +#define DUK_TVAL_SET_BOOLEAN_FALSE(tv) DUK_TVAL_SET_BOOLEAN((tv), 0) + +#define DUK_TVAL_STRING_IS_SYMBOL(tv) \ + DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING((tv))) /* Lightfunc flags packing and unpacking. */ -/* Sign extend: 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss## */ +/* Sign extend: 0x0000##00 -> 0x##000000 -> sign extend to 0xssssss##. + * Avoid signed shifts due to portability limitations. + */ #define DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags) \ - ((((duk_int32_t) (lf_flags)) << 16) >> 24) + ((duk_int32_t) (duk_int8_t) (((duk_uint16_t) (lf_flags)) >> 8)) #define DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags) \ - (((lf_flags) >> 4) & 0x0f) + (((lf_flags) >> 4) & 0x0fU) #define DUK_LFUNC_FLAGS_GET_NARGS(lf_flags) \ - ((lf_flags) & 0x0f) + ((lf_flags) & 0x0fU) #define DUK_LFUNC_FLAGS_PACK(magic,length,nargs) \ - (((magic) & 0xff) << 8) | ((length) << 4) | (nargs) + ((((duk_small_uint_t) (magic)) & 0xffU) << 8) | ((length) << 4) | (nargs) #define DUK_LFUNC_NARGS_VARARGS 0x0f /* varargs marker */ #define DUK_LFUNC_NARGS_MIN 0x00 @@ -864,24 +1507,32 @@ DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv /* fastint constants etc */ #if defined(DUK_USE_FASTINT) -#define DUK_FASTINT_MIN (-0x800000000000LL) -#define DUK_FASTINT_MAX 0x7fffffffffffLL +#define DUK_FASTINT_MIN (DUK_I64_CONSTANT(-0x800000000000)) +#define DUK_FASTINT_MAX (DUK_I64_CONSTANT(0x7fffffffffff)) #define DUK_FASTINT_BITS 48 -DUK_INTERNAL_DECL void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x); +DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x); +DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double_t x); +#endif + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_tval_assert_valid(duk_tval *tv); +#define DUK_TVAL_ASSERT_VALID(tv) do { duk_tval_assert_valid((tv)); } while (0) +#else +#define DUK_TVAL_ASSERT_VALID(tv) do {} while (0) #endif #endif /* DUK_TVAL_H_INCLUDED */ -#line 1 "duk_builtins.h" +/* #include duk_builtins.h */ /* * Automatically generated by genbuiltins.py, do not edit! */ -#ifndef DUK_BUILTINS_H_INCLUDED +#if !defined(DUK_BUILTINS_H_INCLUDED) #define DUK_BUILTINS_H_INCLUDED #if defined(DUK_USE_ROM_STRINGS) -#error ROM support not enabled, rerun make_dist.py with --rom-support +#error ROM support not enabled, rerun configure.py with --rom-support #else /* DUK_USE_ROM_STRINGS */ #define DUK_STRIDX_UC_UNDEFINED 0 /* 'Undefined' */ #define DUK_HEAP_STRING_UC_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_UNDEFINED) @@ -889,102 +1540,102 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) #define DUK_STRIDX_UC_NULL 1 /* 'Null' */ #define DUK_HEAP_STRING_UC_NULL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NULL) #define DUK_HTHREAD_STRING_UC_NULL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NULL) -#define DUK_STRIDX_UC_ARGUMENTS 2 /* 'Arguments' */ +#define DUK_STRIDX_UC_SYMBOL 2 /* 'Symbol' */ +#define DUK_HEAP_STRING_UC_SYMBOL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_SYMBOL) +#define DUK_HTHREAD_STRING_UC_SYMBOL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_SYMBOL) +#define DUK_STRIDX_UC_ARGUMENTS 3 /* 'Arguments' */ #define DUK_HEAP_STRING_UC_ARGUMENTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARGUMENTS) #define DUK_HTHREAD_STRING_UC_ARGUMENTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARGUMENTS) -#define DUK_STRIDX_UC_OBJECT 3 /* 'Object' */ +#define DUK_STRIDX_UC_OBJECT 4 /* 'Object' */ #define DUK_HEAP_STRING_UC_OBJECT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_OBJECT) #define DUK_HTHREAD_STRING_UC_OBJECT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_OBJECT) -#define DUK_STRIDX_UC_FUNCTION 4 /* 'Function' */ +#define DUK_STRIDX_UC_FUNCTION 5 /* 'Function' */ #define DUK_HEAP_STRING_UC_FUNCTION(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_FUNCTION) #define DUK_HTHREAD_STRING_UC_FUNCTION(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_FUNCTION) -#define DUK_STRIDX_ARRAY 5 /* 'Array' */ -#define DUK_HEAP_STRING_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ARRAY) -#define DUK_HTHREAD_STRING_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ARRAY) -#define DUK_STRIDX_UC_STRING 6 /* 'String' */ +#define DUK_STRIDX_UC_ARRAY 6 /* 'Array' */ +#define DUK_HEAP_STRING_UC_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ARRAY) +#define DUK_HTHREAD_STRING_UC_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ARRAY) +#define DUK_STRIDX_UC_STRING 7 /* 'String' */ #define DUK_HEAP_STRING_UC_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_STRING) #define DUK_HTHREAD_STRING_UC_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_STRING) -#define DUK_STRIDX_UC_BOOLEAN 7 /* 'Boolean' */ +#define DUK_STRIDX_UC_BOOLEAN 8 /* 'Boolean' */ #define DUK_HEAP_STRING_UC_BOOLEAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BOOLEAN) #define DUK_HTHREAD_STRING_UC_BOOLEAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BOOLEAN) -#define DUK_STRIDX_UC_NUMBER 8 /* 'Number' */ +#define DUK_STRIDX_UC_NUMBER 9 /* 'Number' */ #define DUK_HEAP_STRING_UC_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_NUMBER) #define DUK_HTHREAD_STRING_UC_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_NUMBER) -#define DUK_STRIDX_DATE 9 /* 'Date' */ -#define DUK_HEAP_STRING_DATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATE) -#define DUK_HTHREAD_STRING_DATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATE) -#define DUK_STRIDX_REG_EXP 10 /* 'RegExp' */ +#define DUK_STRIDX_UC_DATE 10 /* 'Date' */ +#define DUK_HEAP_STRING_UC_DATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_DATE) +#define DUK_HTHREAD_STRING_UC_DATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_DATE) +#define DUK_STRIDX_REG_EXP 11 /* 'RegExp' */ #define DUK_HEAP_STRING_REG_EXP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_REG_EXP) #define DUK_HTHREAD_STRING_REG_EXP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_REG_EXP) -#define DUK_STRIDX_UC_ERROR 11 /* 'Error' */ +#define DUK_STRIDX_UC_ERROR 12 /* 'Error' */ #define DUK_HEAP_STRING_UC_ERROR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_ERROR) #define DUK_HTHREAD_STRING_UC_ERROR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_ERROR) -#define DUK_STRIDX_MATH 12 /* 'Math' */ +#define DUK_STRIDX_MATH 13 /* 'Math' */ #define DUK_HEAP_STRING_MATH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MATH) #define DUK_HTHREAD_STRING_MATH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MATH) -#define DUK_STRIDX_JSON 13 /* 'JSON' */ +#define DUK_STRIDX_JSON 14 /* 'JSON' */ #define DUK_HEAP_STRING_JSON(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON) #define DUK_HTHREAD_STRING_JSON(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON) -#define DUK_STRIDX_EMPTY_STRING 14 /* '' */ +#define DUK_STRIDX_EMPTY_STRING 15 /* '' */ #define DUK_HEAP_STRING_EMPTY_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EMPTY_STRING) #define DUK_HTHREAD_STRING_EMPTY_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EMPTY_STRING) -#define DUK_STRIDX_ARRAY_BUFFER 15 /* 'ArrayBuffer' */ +#define DUK_STRIDX_ARRAY_BUFFER 16 /* 'ArrayBuffer' */ #define DUK_HEAP_STRING_ARRAY_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ARRAY_BUFFER) #define DUK_HTHREAD_STRING_ARRAY_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ARRAY_BUFFER) -#define DUK_STRIDX_DATA_VIEW 16 /* 'DataView' */ +#define DUK_STRIDX_DATA_VIEW 17 /* 'DataView' */ #define DUK_HEAP_STRING_DATA_VIEW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA_VIEW) #define DUK_HTHREAD_STRING_DATA_VIEW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA_VIEW) -#define DUK_STRIDX_INT8_ARRAY 17 /* 'Int8Array' */ +#define DUK_STRIDX_INT8_ARRAY 18 /* 'Int8Array' */ #define DUK_HEAP_STRING_INT8_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT8_ARRAY) #define DUK_HTHREAD_STRING_INT8_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT8_ARRAY) -#define DUK_STRIDX_UINT8_ARRAY 18 /* 'Uint8Array' */ +#define DUK_STRIDX_UINT8_ARRAY 19 /* 'Uint8Array' */ #define DUK_HEAP_STRING_UINT8_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_ARRAY) #define DUK_HTHREAD_STRING_UINT8_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_ARRAY) -#define DUK_STRIDX_UINT8_CLAMPED_ARRAY 19 /* 'Uint8ClampedArray' */ +#define DUK_STRIDX_UINT8_CLAMPED_ARRAY 20 /* 'Uint8ClampedArray' */ #define DUK_HEAP_STRING_UINT8_CLAMPED_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT8_CLAMPED_ARRAY) #define DUK_HTHREAD_STRING_UINT8_CLAMPED_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT8_CLAMPED_ARRAY) -#define DUK_STRIDX_INT16_ARRAY 20 /* 'Int16Array' */ +#define DUK_STRIDX_INT16_ARRAY 21 /* 'Int16Array' */ #define DUK_HEAP_STRING_INT16_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT16_ARRAY) #define DUK_HTHREAD_STRING_INT16_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT16_ARRAY) -#define DUK_STRIDX_UINT16_ARRAY 21 /* 'Uint16Array' */ +#define DUK_STRIDX_UINT16_ARRAY 22 /* 'Uint16Array' */ #define DUK_HEAP_STRING_UINT16_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT16_ARRAY) #define DUK_HTHREAD_STRING_UINT16_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT16_ARRAY) -#define DUK_STRIDX_INT32_ARRAY 22 /* 'Int32Array' */ +#define DUK_STRIDX_INT32_ARRAY 23 /* 'Int32Array' */ #define DUK_HEAP_STRING_INT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT32_ARRAY) #define DUK_HTHREAD_STRING_INT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT32_ARRAY) -#define DUK_STRIDX_UINT32_ARRAY 23 /* 'Uint32Array' */ +#define DUK_STRIDX_UINT32_ARRAY 24 /* 'Uint32Array' */ #define DUK_HEAP_STRING_UINT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UINT32_ARRAY) #define DUK_HTHREAD_STRING_UINT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UINT32_ARRAY) -#define DUK_STRIDX_FLOAT32_ARRAY 24 /* 'Float32Array' */ +#define DUK_STRIDX_FLOAT32_ARRAY 25 /* 'Float32Array' */ #define DUK_HEAP_STRING_FLOAT32_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT32_ARRAY) #define DUK_HTHREAD_STRING_FLOAT32_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT32_ARRAY) -#define DUK_STRIDX_FLOAT64_ARRAY 25 /* 'Float64Array' */ +#define DUK_STRIDX_FLOAT64_ARRAY 26 /* 'Float64Array' */ #define DUK_HEAP_STRING_FLOAT64_ARRAY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLOAT64_ARRAY) #define DUK_HTHREAD_STRING_FLOAT64_ARRAY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLOAT64_ARRAY) -#define DUK_STRIDX_GLOBAL 26 /* 'global' */ +#define DUK_STRIDX_GLOBAL 27 /* 'global' */ #define DUK_HEAP_STRING_GLOBAL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GLOBAL) #define DUK_HTHREAD_STRING_GLOBAL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GLOBAL) -#define DUK_STRIDX_OBJ_ENV 27 /* 'ObjEnv' */ +#define DUK_STRIDX_OBJ_ENV 28 /* 'ObjEnv' */ #define DUK_HEAP_STRING_OBJ_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OBJ_ENV) #define DUK_HTHREAD_STRING_OBJ_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OBJ_ENV) -#define DUK_STRIDX_DEC_ENV 28 /* 'DecEnv' */ +#define DUK_STRIDX_DEC_ENV 29 /* 'DecEnv' */ #define DUK_HEAP_STRING_DEC_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEC_ENV) #define DUK_HTHREAD_STRING_DEC_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEC_ENV) -#define DUK_STRIDX_UC_BUFFER 29 /* 'Buffer' */ +#define DUK_STRIDX_UC_BUFFER 30 /* 'Buffer' */ #define DUK_HEAP_STRING_UC_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_BUFFER) #define DUK_HTHREAD_STRING_UC_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_BUFFER) -#define DUK_STRIDX_UC_POINTER 30 /* 'Pointer' */ +#define DUK_STRIDX_UC_POINTER 31 /* 'Pointer' */ #define DUK_HEAP_STRING_UC_POINTER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_POINTER) #define DUK_HTHREAD_STRING_UC_POINTER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_POINTER) -#define DUK_STRIDX_UC_THREAD 31 /* 'Thread' */ +#define DUK_STRIDX_UC_THREAD 32 /* 'Thread' */ #define DUK_HEAP_STRING_UC_THREAD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_UC_THREAD) #define DUK_HTHREAD_STRING_UC_THREAD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_UC_THREAD) -#define DUK_STRIDX_EVAL 32 /* 'eval' */ +#define DUK_STRIDX_EVAL 33 /* 'eval' */ #define DUK_HEAP_STRING_EVAL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EVAL) #define DUK_HTHREAD_STRING_EVAL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EVAL) -#define DUK_STRIDX_DEFINE_PROPERTY 33 /* 'defineProperty' */ -#define DUK_HEAP_STRING_DEFINE_PROPERTY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEFINE_PROPERTY) -#define DUK_HTHREAD_STRING_DEFINE_PROPERTY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEFINE_PROPERTY) #define DUK_STRIDX_VALUE 34 /* 'value' */ #define DUK_HEAP_STRING_VALUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VALUE) #define DUK_HTHREAD_STRING_VALUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VALUE) @@ -1027,9 +1678,9 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) #define DUK_STRIDX_LAST_INDEX 47 /* 'lastIndex' */ #define DUK_HEAP_STRING_LAST_INDEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LAST_INDEX) #define DUK_HTHREAD_STRING_LAST_INDEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LAST_INDEX) -#define DUK_STRIDX_ESCAPED_EMPTY_REGEXP 48 /* '(?:)' */ -#define DUK_HEAP_STRING_ESCAPED_EMPTY_REGEXP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ESCAPED_EMPTY_REGEXP) -#define DUK_HTHREAD_STRING_ESCAPED_EMPTY_REGEXP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ESCAPED_EMPTY_REGEXP) +#define DUK_STRIDX_FLAGS 48 /* 'flags' */ +#define DUK_HEAP_STRING_FLAGS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FLAGS) +#define DUK_HTHREAD_STRING_FLAGS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FLAGS) #define DUK_STRIDX_INDEX 49 /* 'index' */ #define DUK_HEAP_STRING_INDEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INDEX) #define DUK_HTHREAD_STRING_INDEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INDEX) @@ -1051,30 +1702,30 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) #define DUK_STRIDX_LC_STRING 55 /* 'string' */ #define DUK_HEAP_STRING_LC_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_STRING) #define DUK_HTHREAD_STRING_LC_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_STRING) -#define DUK_STRIDX_LC_OBJECT 56 /* 'object' */ +#define DUK_STRIDX_LC_SYMBOL 56 /* 'symbol' */ +#define DUK_HEAP_STRING_LC_SYMBOL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_SYMBOL) +#define DUK_HTHREAD_STRING_LC_SYMBOL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_SYMBOL) +#define DUK_STRIDX_LC_OBJECT 57 /* 'object' */ #define DUK_HEAP_STRING_LC_OBJECT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_OBJECT) #define DUK_HTHREAD_STRING_LC_OBJECT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_OBJECT) -#define DUK_STRIDX_LC_UNDEFINED 57 /* 'undefined' */ +#define DUK_STRIDX_LC_UNDEFINED 58 /* 'undefined' */ #define DUK_HEAP_STRING_LC_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_UNDEFINED) #define DUK_HTHREAD_STRING_LC_UNDEFINED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_UNDEFINED) -#define DUK_STRIDX_NAN 58 /* 'NaN' */ +#define DUK_STRIDX_NAN 59 /* 'NaN' */ #define DUK_HEAP_STRING_NAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAN) #define DUK_HTHREAD_STRING_NAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAN) -#define DUK_STRIDX_INFINITY 59 /* 'Infinity' */ +#define DUK_STRIDX_INFINITY 60 /* 'Infinity' */ #define DUK_HEAP_STRING_INFINITY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INFINITY) #define DUK_HTHREAD_STRING_INFINITY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INFINITY) -#define DUK_STRIDX_MINUS_INFINITY 60 /* '-Infinity' */ +#define DUK_STRIDX_MINUS_INFINITY 61 /* '-Infinity' */ #define DUK_HEAP_STRING_MINUS_INFINITY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_INFINITY) #define DUK_HTHREAD_STRING_MINUS_INFINITY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_INFINITY) -#define DUK_STRIDX_MINUS_ZERO 61 /* '-0' */ +#define DUK_STRIDX_MINUS_ZERO 62 /* '-0' */ #define DUK_HEAP_STRING_MINUS_ZERO(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MINUS_ZERO) #define DUK_HTHREAD_STRING_MINUS_ZERO(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MINUS_ZERO) -#define DUK_STRIDX_COMMA 62 /* ',' */ +#define DUK_STRIDX_COMMA 63 /* ',' */ #define DUK_HEAP_STRING_COMMA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMMA) #define DUK_HTHREAD_STRING_COMMA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMMA) -#define DUK_STRIDX_SPACE 63 /* ' ' */ -#define DUK_HEAP_STRING_SPACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SPACE) -#define DUK_HTHREAD_STRING_SPACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SPACE) #define DUK_STRIDX_NEWLINE_4SPACE 64 /* '\n ' */ #define DUK_HEAP_STRING_NEWLINE_4SPACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEWLINE_4SPACE) #define DUK_HTHREAD_STRING_NEWLINE_4SPACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEWLINE_4SPACE) @@ -1093,388 +1744,316 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast(duk_tval *tv, duk_double_t x) #define DUK_STRIDX_CALLER 69 /* 'caller' */ #define DUK_HEAP_STRING_CALLER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLER) #define DUK_HTHREAD_STRING_CALLER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLER) -#define DUK_STRIDX_HAS 70 /* 'has' */ -#define DUK_HEAP_STRING_HAS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HAS) -#define DUK_HTHREAD_STRING_HAS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HAS) -#define DUK_STRIDX_GET 71 /* 'get' */ -#define DUK_HEAP_STRING_GET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GET) -#define DUK_HTHREAD_STRING_GET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GET) +#define DUK_STRIDX_APPLY 70 /* 'apply' */ +#define DUK_HEAP_STRING_APPLY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_APPLY) +#define DUK_HTHREAD_STRING_APPLY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_APPLY) +#define DUK_STRIDX_CONSTRUCT 71 /* 'construct' */ +#define DUK_HEAP_STRING_CONSTRUCT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCT) +#define DUK_HTHREAD_STRING_CONSTRUCT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCT) #define DUK_STRIDX_DELETE_PROPERTY 72 /* 'deleteProperty' */ #define DUK_HEAP_STRING_DELETE_PROPERTY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE_PROPERTY) #define DUK_HTHREAD_STRING_DELETE_PROPERTY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE_PROPERTY) -#define DUK_STRIDX_ENUMERATE 73 /* 'enumerate' */ -#define DUK_HEAP_STRING_ENUMERATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUMERATE) -#define DUK_HTHREAD_STRING_ENUMERATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUMERATE) -#define DUK_STRIDX_OWN_KEYS 74 /* 'ownKeys' */ +#define DUK_STRIDX_GET 73 /* 'get' */ +#define DUK_HEAP_STRING_GET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GET) +#define DUK_HTHREAD_STRING_GET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GET) +#define DUK_STRIDX_HAS 74 /* 'has' */ +#define DUK_HEAP_STRING_HAS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HAS) +#define DUK_HTHREAD_STRING_HAS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HAS) +#define DUK_STRIDX_OWN_KEYS 75 /* 'ownKeys' */ #define DUK_HEAP_STRING_OWN_KEYS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OWN_KEYS) #define DUK_HTHREAD_STRING_OWN_KEYS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OWN_KEYS) -#define DUK_STRIDX_SET_PROTOTYPE_OF 75 /* 'setPrototypeOf' */ +#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE 76 /* '\x81Symbol.toPrimitive\xff' */ +#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE) +#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_PRIMITIVE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE) +#define DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE 77 /* '\x81Symbol.hasInstance\xff' */ +#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE) +#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_HAS_INSTANCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE) +#define DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG 78 /* '\x81Symbol.toStringTag\xff' */ +#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG) +#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_TO_STRING_TAG(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG) +#define DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE 79 /* '\x81Symbol.isConcatSpreadable\xff' */ +#define DUK_HEAP_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE) +#define DUK_HTHREAD_STRING_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE) +#define DUK_STRIDX_SET_PROTOTYPE_OF 80 /* 'setPrototypeOf' */ #define DUK_HEAP_STRING_SET_PROTOTYPE_OF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET_PROTOTYPE_OF) #define DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET_PROTOTYPE_OF) -#define DUK_STRIDX___PROTO__ 76 /* '__proto__' */ +#define DUK_STRIDX___PROTO__ 81 /* '__proto__' */ #define DUK_HEAP_STRING___PROTO__(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX___PROTO__) #define DUK_HTHREAD_STRING___PROTO__(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX___PROTO__) -#define DUK_STRIDX_REQUIRE 77 /* 'require' */ -#define DUK_HEAP_STRING_REQUIRE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_REQUIRE) -#define DUK_HTHREAD_STRING_REQUIRE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_REQUIRE) -#define DUK_STRIDX_ID 78 /* 'id' */ -#define DUK_HEAP_STRING_ID(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ID) -#define DUK_HTHREAD_STRING_ID(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ID) -#define DUK_STRIDX_EXPORTS 79 /* 'exports' */ -#define DUK_HEAP_STRING_EXPORTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXPORTS) -#define DUK_HTHREAD_STRING_EXPORTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXPORTS) -#define DUK_STRIDX_FILENAME 80 /* 'filename' */ -#define DUK_HEAP_STRING_FILENAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILENAME) -#define DUK_HTHREAD_STRING_FILENAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILENAME) -#define DUK_STRIDX_TO_STRING 81 /* 'toString' */ +#define DUK_STRIDX_TO_STRING 82 /* 'toString' */ #define DUK_HEAP_STRING_TO_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_STRING) #define DUK_HTHREAD_STRING_TO_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_STRING) -#define DUK_STRIDX_TO_JSON 82 /* 'toJSON' */ +#define DUK_STRIDX_TO_JSON 83 /* 'toJSON' */ #define DUK_HEAP_STRING_TO_JSON(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_JSON) #define DUK_HTHREAD_STRING_TO_JSON(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_JSON) -#define DUK_STRIDX_TYPE 83 /* 'type' */ +#define DUK_STRIDX_TYPE 84 /* 'type' */ #define DUK_HEAP_STRING_TYPE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPE) #define DUK_HTHREAD_STRING_TYPE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPE) -#define DUK_STRIDX_DATA 84 /* 'data' */ +#define DUK_STRIDX_DATA 85 /* 'data' */ #define DUK_HEAP_STRING_DATA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA) #define DUK_HTHREAD_STRING_DATA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA) -#define DUK_STRIDX_LENGTH 85 /* 'length' */ -#define DUK_HEAP_STRING_LENGTH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LENGTH) -#define DUK_HTHREAD_STRING_LENGTH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LENGTH) -#define DUK_STRIDX_BYTE_LENGTH 86 /* 'byteLength' */ -#define DUK_HEAP_STRING_BYTE_LENGTH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BYTE_LENGTH) -#define DUK_HTHREAD_STRING_BYTE_LENGTH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BYTE_LENGTH) -#define DUK_STRIDX_BYTE_OFFSET 87 /* 'byteOffset' */ -#define DUK_HEAP_STRING_BYTE_OFFSET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BYTE_OFFSET) -#define DUK_HTHREAD_STRING_BYTE_OFFSET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BYTE_OFFSET) -#define DUK_STRIDX_BYTES_PER_ELEMENT 88 /* 'BYTES_PER_ELEMENT' */ -#define DUK_HEAP_STRING_BYTES_PER_ELEMENT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BYTES_PER_ELEMENT) -#define DUK_HTHREAD_STRING_BYTES_PER_ELEMENT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BYTES_PER_ELEMENT) -#define DUK_STRIDX_SET 89 /* 'set' */ -#define DUK_HEAP_STRING_SET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET) -#define DUK_HTHREAD_STRING_SET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET) -#define DUK_STRIDX_STACK 90 /* 'stack' */ -#define DUK_HEAP_STRING_STACK(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STACK) -#define DUK_HTHREAD_STRING_STACK(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STACK) -#define DUK_STRIDX_PC 91 /* 'pc' */ -#define DUK_HEAP_STRING_PC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PC) -#define DUK_HTHREAD_STRING_PC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PC) -#define DUK_STRIDX_LINE_NUMBER 92 /* 'lineNumber' */ -#define DUK_HEAP_STRING_LINE_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LINE_NUMBER) -#define DUK_HTHREAD_STRING_LINE_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LINE_NUMBER) -#define DUK_STRIDX_INT_TRACEDATA 93 /* '\xffTracedata' */ -#define DUK_HEAP_STRING_INT_TRACEDATA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TRACEDATA) -#define DUK_HTHREAD_STRING_INT_TRACEDATA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TRACEDATA) -#define DUK_STRIDX_NAME 94 /* 'name' */ -#define DUK_HEAP_STRING_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAME) -#define DUK_HTHREAD_STRING_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAME) -#define DUK_STRIDX_FILE_NAME 95 /* 'fileName' */ -#define DUK_HEAP_STRING_FILE_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILE_NAME) -#define DUK_HTHREAD_STRING_FILE_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILE_NAME) -#define DUK_STRIDX_LC_BUFFER 96 /* 'buffer' */ +#define DUK_STRIDX_LC_BUFFER 86 /* 'buffer' */ #define DUK_HEAP_STRING_LC_BUFFER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_BUFFER) #define DUK_HTHREAD_STRING_LC_BUFFER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_BUFFER) -#define DUK_STRIDX_LC_POINTER 97 /* 'pointer' */ +#define DUK_STRIDX_LENGTH 87 /* 'length' */ +#define DUK_HEAP_STRING_LENGTH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LENGTH) +#define DUK_HTHREAD_STRING_LENGTH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LENGTH) +#define DUK_STRIDX_SET 88 /* 'set' */ +#define DUK_HEAP_STRING_SET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET) +#define DUK_HTHREAD_STRING_SET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET) +#define DUK_STRIDX_STACK 89 /* 'stack' */ +#define DUK_HEAP_STRING_STACK(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STACK) +#define DUK_HTHREAD_STRING_STACK(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STACK) +#define DUK_STRIDX_PC 90 /* 'pc' */ +#define DUK_HEAP_STRING_PC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PC) +#define DUK_HTHREAD_STRING_PC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PC) +#define DUK_STRIDX_LINE_NUMBER 91 /* 'lineNumber' */ +#define DUK_HEAP_STRING_LINE_NUMBER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LINE_NUMBER) +#define DUK_HTHREAD_STRING_LINE_NUMBER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LINE_NUMBER) +#define DUK_STRIDX_INT_TRACEDATA 92 /* '\x82Tracedata' */ +#define DUK_HEAP_STRING_INT_TRACEDATA(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TRACEDATA) +#define DUK_HTHREAD_STRING_INT_TRACEDATA(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TRACEDATA) +#define DUK_STRIDX_NAME 93 /* 'name' */ +#define DUK_HEAP_STRING_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAME) +#define DUK_HTHREAD_STRING_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAME) +#define DUK_STRIDX_FILE_NAME 94 /* 'fileName' */ +#define DUK_HEAP_STRING_FILE_NAME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILE_NAME) +#define DUK_HTHREAD_STRING_FILE_NAME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILE_NAME) +#define DUK_STRIDX_LC_POINTER 95 /* 'pointer' */ #define DUK_HEAP_STRING_LC_POINTER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_POINTER) #define DUK_HTHREAD_STRING_LC_POINTER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_POINTER) -#define DUK_STRIDX_INT_VALUE 98 /* '\xffValue' */ -#define DUK_HEAP_STRING_INT_VALUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE) -#define DUK_HTHREAD_STRING_INT_VALUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE) -#define DUK_STRIDX_INT_NEXT 99 /* '\xffNext' */ -#define DUK_HEAP_STRING_INT_NEXT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_NEXT) -#define DUK_HTHREAD_STRING_INT_NEXT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_NEXT) -#define DUK_STRIDX_INT_BYTECODE 100 /* '\xffBytecode' */ -#define DUK_HEAP_STRING_INT_BYTECODE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_BYTECODE) -#define DUK_HTHREAD_STRING_INT_BYTECODE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_BYTECODE) -#define DUK_STRIDX_INT_FORMALS 101 /* '\xffFormals' */ -#define DUK_HEAP_STRING_INT_FORMALS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FORMALS) -#define DUK_HTHREAD_STRING_INT_FORMALS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FORMALS) -#define DUK_STRIDX_INT_VARMAP 102 /* '\xffVarmap' */ -#define DUK_HEAP_STRING_INT_VARMAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARMAP) -#define DUK_HTHREAD_STRING_INT_VARMAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARMAP) -#define DUK_STRIDX_INT_LEXENV 103 /* '\xffLexenv' */ -#define DUK_HEAP_STRING_INT_LEXENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_LEXENV) -#define DUK_HTHREAD_STRING_INT_LEXENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_LEXENV) -#define DUK_STRIDX_INT_VARENV 104 /* '\xffVarenv' */ -#define DUK_HEAP_STRING_INT_VARENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARENV) -#define DUK_HTHREAD_STRING_INT_VARENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARENV) -#define DUK_STRIDX_INT_SOURCE 105 /* '\xffSource' */ -#define DUK_HEAP_STRING_INT_SOURCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_SOURCE) -#define DUK_HTHREAD_STRING_INT_SOURCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_SOURCE) -#define DUK_STRIDX_INT_PC2LINE 106 /* '\xffPc2line' */ -#define DUK_HEAP_STRING_INT_PC2LINE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_PC2LINE) -#define DUK_HTHREAD_STRING_INT_PC2LINE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_PC2LINE) -#define DUK_STRIDX_INT_ARGS 107 /* '\xffArgs' */ -#define DUK_HEAP_STRING_INT_ARGS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_ARGS) -#define DUK_HTHREAD_STRING_INT_ARGS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_ARGS) -#define DUK_STRIDX_INT_MAP 108 /* '\xffMap' */ -#define DUK_HEAP_STRING_INT_MAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_MAP) -#define DUK_HTHREAD_STRING_INT_MAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_MAP) -#define DUK_STRIDX_INT_FINALIZER 109 /* '\xffFinalizer' */ -#define DUK_HEAP_STRING_INT_FINALIZER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FINALIZER) -#define DUK_HTHREAD_STRING_INT_FINALIZER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FINALIZER) -#define DUK_STRIDX_INT_HANDLER 110 /* '\xffHandler' */ -#define DUK_HEAP_STRING_INT_HANDLER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_HANDLER) -#define DUK_HTHREAD_STRING_INT_HANDLER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_HANDLER) -#define DUK_STRIDX_INT_CALLEE 111 /* '\xffCallee' */ -#define DUK_HEAP_STRING_INT_CALLEE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_CALLEE) -#define DUK_HTHREAD_STRING_INT_CALLEE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_CALLEE) -#define DUK_STRIDX_INT_THREAD 112 /* '\xffThread' */ -#define DUK_HEAP_STRING_INT_THREAD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_THREAD) -#define DUK_HTHREAD_STRING_INT_THREAD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_THREAD) -#define DUK_STRIDX_INT_REGBASE 113 /* '\xffRegbase' */ -#define DUK_HEAP_STRING_INT_REGBASE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_REGBASE) -#define DUK_HTHREAD_STRING_INT_REGBASE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_REGBASE) -#define DUK_STRIDX_INT_TARGET 114 /* '\xffTarget' */ +#define DUK_STRIDX_INT_TARGET 96 /* '\x82Target' */ #define DUK_HEAP_STRING_INT_TARGET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TARGET) #define DUK_HTHREAD_STRING_INT_TARGET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TARGET) -#define DUK_STRIDX_INT_THIS 115 /* '\xffThis' */ -#define DUK_HEAP_STRING_INT_THIS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_THIS) -#define DUK_HTHREAD_STRING_INT_THIS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_THIS) -#define DUK_STRIDX_COMPILE 116 /* 'compile' */ +#define DUK_STRIDX_INT_NEXT 97 /* '\x82Next' */ +#define DUK_HEAP_STRING_INT_NEXT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_NEXT) +#define DUK_HTHREAD_STRING_INT_NEXT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_NEXT) +#define DUK_STRIDX_INT_BYTECODE 98 /* '\x82Bytecode' */ +#define DUK_HEAP_STRING_INT_BYTECODE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_BYTECODE) +#define DUK_HTHREAD_STRING_INT_BYTECODE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_BYTECODE) +#define DUK_STRIDX_INT_FORMALS 99 /* '\x82Formals' */ +#define DUK_HEAP_STRING_INT_FORMALS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FORMALS) +#define DUK_HTHREAD_STRING_INT_FORMALS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FORMALS) +#define DUK_STRIDX_INT_VARMAP 100 /* '\x82Varmap' */ +#define DUK_HEAP_STRING_INT_VARMAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARMAP) +#define DUK_HTHREAD_STRING_INT_VARMAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARMAP) +#define DUK_STRIDX_INT_SOURCE 101 /* '\x82Source' */ +#define DUK_HEAP_STRING_INT_SOURCE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_SOURCE) +#define DUK_HTHREAD_STRING_INT_SOURCE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_SOURCE) +#define DUK_STRIDX_INT_PC2LINE 102 /* '\x82Pc2line' */ +#define DUK_HEAP_STRING_INT_PC2LINE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_PC2LINE) +#define DUK_HTHREAD_STRING_INT_PC2LINE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_PC2LINE) +#define DUK_STRIDX_INT_MAP 103 /* '\x82Map' */ +#define DUK_HEAP_STRING_INT_MAP(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_MAP) +#define DUK_HTHREAD_STRING_INT_MAP(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_MAP) +#define DUK_STRIDX_INT_VARENV 104 /* '\x82Varenv' */ +#define DUK_HEAP_STRING_INT_VARENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARENV) +#define DUK_HTHREAD_STRING_INT_VARENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARENV) +#define DUK_STRIDX_INT_FINALIZER 105 /* '\x82Finalizer' */ +#define DUK_HEAP_STRING_INT_FINALIZER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FINALIZER) +#define DUK_HTHREAD_STRING_INT_FINALIZER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FINALIZER) +#define DUK_STRIDX_INT_VALUE 106 /* '\x82Value' */ +#define DUK_HEAP_STRING_INT_VALUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE) +#define DUK_HTHREAD_STRING_INT_VALUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE) +#define DUK_STRIDX_COMPILE 107 /* 'compile' */ #define DUK_HEAP_STRING_COMPILE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMPILE) #define DUK_HTHREAD_STRING_COMPILE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMPILE) -#define DUK_STRIDX_INPUT 117 /* 'input' */ +#define DUK_STRIDX_INPUT 108 /* 'input' */ #define DUK_HEAP_STRING_INPUT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INPUT) #define DUK_HTHREAD_STRING_INPUT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INPUT) -#define DUK_STRIDX_ERR_CREATE 118 /* 'errCreate' */ +#define DUK_STRIDX_ERR_CREATE 109 /* 'errCreate' */ #define DUK_HEAP_STRING_ERR_CREATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_CREATE) #define DUK_HTHREAD_STRING_ERR_CREATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_CREATE) -#define DUK_STRIDX_ERR_THROW 119 /* 'errThrow' */ +#define DUK_STRIDX_ERR_THROW 110 /* 'errThrow' */ #define DUK_HEAP_STRING_ERR_THROW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_THROW) #define DUK_HTHREAD_STRING_ERR_THROW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_THROW) -#define DUK_STRIDX_MOD_SEARCH 120 /* 'modSearch' */ -#define DUK_HEAP_STRING_MOD_SEARCH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MOD_SEARCH) -#define DUK_HTHREAD_STRING_MOD_SEARCH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MOD_SEARCH) -#define DUK_STRIDX_MOD_LOADED 121 /* 'modLoaded' */ -#define DUK_HEAP_STRING_MOD_LOADED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_MOD_LOADED) -#define DUK_HTHREAD_STRING_MOD_LOADED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_MOD_LOADED) -#define DUK_STRIDX_ENV 122 /* 'env' */ +#define DUK_STRIDX_ENV 111 /* 'env' */ #define DUK_HEAP_STRING_ENV(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENV) #define DUK_HTHREAD_STRING_ENV(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENV) -#define DUK_STRIDX_HEX 123 /* 'hex' */ +#define DUK_STRIDX_HEX 112 /* 'hex' */ #define DUK_HEAP_STRING_HEX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HEX) #define DUK_HTHREAD_STRING_HEX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HEX) -#define DUK_STRIDX_BASE64 124 /* 'base64' */ +#define DUK_STRIDX_BASE64 113 /* 'base64' */ #define DUK_HEAP_STRING_BASE64(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BASE64) #define DUK_HTHREAD_STRING_BASE64(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BASE64) -#define DUK_STRIDX_JX 125 /* 'jx' */ +#define DUK_STRIDX_JX 114 /* 'jx' */ #define DUK_HEAP_STRING_JX(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JX) #define DUK_HTHREAD_STRING_JX(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JX) -#define DUK_STRIDX_JC 126 /* 'jc' */ +#define DUK_STRIDX_JC 115 /* 'jc' */ #define DUK_HEAP_STRING_JC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JC) #define DUK_HTHREAD_STRING_JC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JC) -#define DUK_STRIDX_RESUME 127 /* 'resume' */ -#define DUK_HEAP_STRING_RESUME(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RESUME) -#define DUK_HTHREAD_STRING_RESUME(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RESUME) -#define DUK_STRIDX_FMT 128 /* 'fmt' */ -#define DUK_HEAP_STRING_FMT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FMT) -#define DUK_HTHREAD_STRING_FMT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FMT) -#define DUK_STRIDX_RAW 129 /* 'raw' */ -#define DUK_HEAP_STRING_RAW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RAW) -#define DUK_HTHREAD_STRING_RAW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RAW) -#define DUK_STRIDX_LC_TRACE 130 /* 'trace' */ -#define DUK_HEAP_STRING_LC_TRACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_TRACE) -#define DUK_HTHREAD_STRING_LC_TRACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_TRACE) -#define DUK_STRIDX_LC_DEBUG 131 /* 'debug' */ -#define DUK_HEAP_STRING_LC_DEBUG(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_DEBUG) -#define DUK_HTHREAD_STRING_LC_DEBUG(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_DEBUG) -#define DUK_STRIDX_LC_INFO 132 /* 'info' */ -#define DUK_HEAP_STRING_LC_INFO(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_INFO) -#define DUK_HTHREAD_STRING_LC_INFO(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_INFO) -#define DUK_STRIDX_LC_WARN 133 /* 'warn' */ -#define DUK_HEAP_STRING_LC_WARN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_WARN) -#define DUK_HTHREAD_STRING_LC_WARN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_WARN) -#define DUK_STRIDX_LC_ERROR 134 /* 'error' */ -#define DUK_HEAP_STRING_LC_ERROR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_ERROR) -#define DUK_HTHREAD_STRING_LC_ERROR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_ERROR) -#define DUK_STRIDX_LC_FATAL 135 /* 'fatal' */ -#define DUK_HEAP_STRING_LC_FATAL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_FATAL) -#define DUK_HTHREAD_STRING_LC_FATAL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_FATAL) -#define DUK_STRIDX_LC_N 136 /* 'n' */ -#define DUK_HEAP_STRING_LC_N(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_N) -#define DUK_HTHREAD_STRING_LC_N(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_N) -#define DUK_STRIDX_LC_L 137 /* 'l' */ -#define DUK_HEAP_STRING_LC_L(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_L) -#define DUK_HTHREAD_STRING_LC_L(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_L) -#define DUK_STRIDX_CLOG 138 /* 'clog' */ -#define DUK_HEAP_STRING_CLOG(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CLOG) -#define DUK_HTHREAD_STRING_CLOG(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CLOG) -#define DUK_STRIDX_TO_LOG_STRING 139 /* 'toLogString' */ -#define DUK_HEAP_STRING_TO_LOG_STRING(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_LOG_STRING) -#define DUK_HTHREAD_STRING_TO_LOG_STRING(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_LOG_STRING) -#define DUK_STRIDX_JSON_EXT_UNDEFINED 140 /* '{"_undef":true}' */ +#define DUK_STRIDX_JSON_EXT_UNDEFINED 116 /* '{"_undef":true}' */ #define DUK_HEAP_STRING_JSON_EXT_UNDEFINED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_UNDEFINED) #define DUK_HTHREAD_STRING_JSON_EXT_UNDEFINED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_UNDEFINED) -#define DUK_STRIDX_JSON_EXT_NAN 141 /* '{"_nan":true}' */ +#define DUK_STRIDX_JSON_EXT_NAN 117 /* '{"_nan":true}' */ #define DUK_HEAP_STRING_JSON_EXT_NAN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NAN) #define DUK_HTHREAD_STRING_JSON_EXT_NAN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NAN) -#define DUK_STRIDX_JSON_EXT_POSINF 142 /* '{"_inf":true}' */ +#define DUK_STRIDX_JSON_EXT_POSINF 118 /* '{"_inf":true}' */ #define DUK_HEAP_STRING_JSON_EXT_POSINF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_POSINF) #define DUK_HTHREAD_STRING_JSON_EXT_POSINF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_POSINF) -#define DUK_STRIDX_JSON_EXT_NEGINF 143 /* '{"_ninf":true}' */ +#define DUK_STRIDX_JSON_EXT_NEGINF 119 /* '{"_ninf":true}' */ #define DUK_HEAP_STRING_JSON_EXT_NEGINF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NEGINF) #define DUK_HTHREAD_STRING_JSON_EXT_NEGINF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NEGINF) -#define DUK_STRIDX_JSON_EXT_FUNCTION1 144 /* '{"_func":true}' */ +#define DUK_STRIDX_JSON_EXT_FUNCTION1 120 /* '{"_func":true}' */ #define DUK_HEAP_STRING_JSON_EXT_FUNCTION1(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION1) #define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION1(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION1) -#define DUK_STRIDX_JSON_EXT_FUNCTION2 145 /* '{_func:true}' */ +#define DUK_STRIDX_JSON_EXT_FUNCTION2 121 /* '{_func:true}' */ #define DUK_HEAP_STRING_JSON_EXT_FUNCTION2(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION2) #define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION2(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION2) -#define DUK_STRIDX_BREAK 146 /* 'break' */ +#define DUK_STRIDX_BREAK 122 /* 'break' */ #define DUK_HEAP_STRING_BREAK(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BREAK) #define DUK_HTHREAD_STRING_BREAK(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BREAK) -#define DUK_STRIDX_CASE 147 /* 'case' */ +#define DUK_STRIDX_CASE 123 /* 'case' */ #define DUK_HEAP_STRING_CASE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CASE) #define DUK_HTHREAD_STRING_CASE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CASE) -#define DUK_STRIDX_CATCH 148 /* 'catch' */ +#define DUK_STRIDX_CATCH 124 /* 'catch' */ #define DUK_HEAP_STRING_CATCH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CATCH) #define DUK_HTHREAD_STRING_CATCH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CATCH) -#define DUK_STRIDX_CONTINUE 149 /* 'continue' */ +#define DUK_STRIDX_CONTINUE 125 /* 'continue' */ #define DUK_HEAP_STRING_CONTINUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONTINUE) #define DUK_HTHREAD_STRING_CONTINUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONTINUE) -#define DUK_STRIDX_DEBUGGER 150 /* 'debugger' */ +#define DUK_STRIDX_DEBUGGER 126 /* 'debugger' */ #define DUK_HEAP_STRING_DEBUGGER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEBUGGER) #define DUK_HTHREAD_STRING_DEBUGGER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEBUGGER) -#define DUK_STRIDX_DEFAULT 151 /* 'default' */ +#define DUK_STRIDX_DEFAULT 127 /* 'default' */ #define DUK_HEAP_STRING_DEFAULT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEFAULT) #define DUK_HTHREAD_STRING_DEFAULT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEFAULT) -#define DUK_STRIDX_DELETE 152 /* 'delete' */ +#define DUK_STRIDX_DELETE 128 /* 'delete' */ #define DUK_HEAP_STRING_DELETE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE) #define DUK_HTHREAD_STRING_DELETE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE) -#define DUK_STRIDX_DO 153 /* 'do' */ +#define DUK_STRIDX_DO 129 /* 'do' */ #define DUK_HEAP_STRING_DO(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DO) #define DUK_HTHREAD_STRING_DO(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DO) -#define DUK_STRIDX_ELSE 154 /* 'else' */ +#define DUK_STRIDX_ELSE 130 /* 'else' */ #define DUK_HEAP_STRING_ELSE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ELSE) #define DUK_HTHREAD_STRING_ELSE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ELSE) -#define DUK_STRIDX_FINALLY 155 /* 'finally' */ +#define DUK_STRIDX_FINALLY 131 /* 'finally' */ #define DUK_HEAP_STRING_FINALLY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FINALLY) #define DUK_HTHREAD_STRING_FINALLY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FINALLY) -#define DUK_STRIDX_FOR 156 /* 'for' */ +#define DUK_STRIDX_FOR 132 /* 'for' */ #define DUK_HEAP_STRING_FOR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FOR) #define DUK_HTHREAD_STRING_FOR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FOR) -#define DUK_STRIDX_LC_FUNCTION 157 /* 'function' */ +#define DUK_STRIDX_LC_FUNCTION 133 /* 'function' */ #define DUK_HEAP_STRING_LC_FUNCTION(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_FUNCTION) #define DUK_HTHREAD_STRING_LC_FUNCTION(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_FUNCTION) -#define DUK_STRIDX_IF 158 /* 'if' */ +#define DUK_STRIDX_IF 134 /* 'if' */ #define DUK_HEAP_STRING_IF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IF) #define DUK_HTHREAD_STRING_IF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IF) -#define DUK_STRIDX_IN 159 /* 'in' */ +#define DUK_STRIDX_IN 135 /* 'in' */ #define DUK_HEAP_STRING_IN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IN) #define DUK_HTHREAD_STRING_IN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IN) -#define DUK_STRIDX_INSTANCEOF 160 /* 'instanceof' */ +#define DUK_STRIDX_INSTANCEOF 136 /* 'instanceof' */ #define DUK_HEAP_STRING_INSTANCEOF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INSTANCEOF) #define DUK_HTHREAD_STRING_INSTANCEOF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INSTANCEOF) -#define DUK_STRIDX_NEW 161 /* 'new' */ +#define DUK_STRIDX_NEW 137 /* 'new' */ #define DUK_HEAP_STRING_NEW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEW) #define DUK_HTHREAD_STRING_NEW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEW) -#define DUK_STRIDX_RETURN 162 /* 'return' */ +#define DUK_STRIDX_RETURN 138 /* 'return' */ #define DUK_HEAP_STRING_RETURN(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RETURN) #define DUK_HTHREAD_STRING_RETURN(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RETURN) -#define DUK_STRIDX_SWITCH 163 /* 'switch' */ +#define DUK_STRIDX_SWITCH 139 /* 'switch' */ #define DUK_HEAP_STRING_SWITCH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SWITCH) #define DUK_HTHREAD_STRING_SWITCH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SWITCH) -#define DUK_STRIDX_THIS 164 /* 'this' */ +#define DUK_STRIDX_THIS 140 /* 'this' */ #define DUK_HEAP_STRING_THIS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THIS) #define DUK_HTHREAD_STRING_THIS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THIS) -#define DUK_STRIDX_THROW 165 /* 'throw' */ +#define DUK_STRIDX_THROW 141 /* 'throw' */ #define DUK_HEAP_STRING_THROW(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THROW) #define DUK_HTHREAD_STRING_THROW(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THROW) -#define DUK_STRIDX_TRY 166 /* 'try' */ +#define DUK_STRIDX_TRY 142 /* 'try' */ #define DUK_HEAP_STRING_TRY(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRY) #define DUK_HTHREAD_STRING_TRY(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRY) -#define DUK_STRIDX_TYPEOF 167 /* 'typeof' */ +#define DUK_STRIDX_TYPEOF 143 /* 'typeof' */ #define DUK_HEAP_STRING_TYPEOF(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPEOF) #define DUK_HTHREAD_STRING_TYPEOF(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPEOF) -#define DUK_STRIDX_VAR 168 /* 'var' */ +#define DUK_STRIDX_VAR 144 /* 'var' */ #define DUK_HEAP_STRING_VAR(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VAR) #define DUK_HTHREAD_STRING_VAR(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VAR) -#define DUK_STRIDX_CONST 169 /* 'const' */ +#define DUK_STRIDX_CONST 145 /* 'const' */ #define DUK_HEAP_STRING_CONST(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONST) #define DUK_HTHREAD_STRING_CONST(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONST) -#define DUK_STRIDX_VOID 170 /* 'void' */ +#define DUK_STRIDX_VOID 146 /* 'void' */ #define DUK_HEAP_STRING_VOID(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VOID) #define DUK_HTHREAD_STRING_VOID(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VOID) -#define DUK_STRIDX_WHILE 171 /* 'while' */ +#define DUK_STRIDX_WHILE 147 /* 'while' */ #define DUK_HEAP_STRING_WHILE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WHILE) #define DUK_HTHREAD_STRING_WHILE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WHILE) -#define DUK_STRIDX_WITH 172 /* 'with' */ +#define DUK_STRIDX_WITH 148 /* 'with' */ #define DUK_HEAP_STRING_WITH(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WITH) #define DUK_HTHREAD_STRING_WITH(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WITH) -#define DUK_STRIDX_CLASS 173 /* 'class' */ +#define DUK_STRIDX_CLASS 149 /* 'class' */ #define DUK_HEAP_STRING_CLASS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CLASS) #define DUK_HTHREAD_STRING_CLASS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CLASS) -#define DUK_STRIDX_ENUM 174 /* 'enum' */ +#define DUK_STRIDX_ENUM 150 /* 'enum' */ #define DUK_HEAP_STRING_ENUM(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUM) #define DUK_HTHREAD_STRING_ENUM(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUM) -#define DUK_STRIDX_EXPORT 175 /* 'export' */ +#define DUK_STRIDX_EXPORT 151 /* 'export' */ #define DUK_HEAP_STRING_EXPORT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXPORT) #define DUK_HTHREAD_STRING_EXPORT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXPORT) -#define DUK_STRIDX_EXTENDS 176 /* 'extends' */ +#define DUK_STRIDX_EXTENDS 152 /* 'extends' */ #define DUK_HEAP_STRING_EXTENDS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXTENDS) #define DUK_HTHREAD_STRING_EXTENDS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXTENDS) -#define DUK_STRIDX_IMPORT 177 /* 'import' */ +#define DUK_STRIDX_IMPORT 153 /* 'import' */ #define DUK_HEAP_STRING_IMPORT(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPORT) #define DUK_HTHREAD_STRING_IMPORT(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPORT) -#define DUK_STRIDX_SUPER 178 /* 'super' */ +#define DUK_STRIDX_SUPER 154 /* 'super' */ #define DUK_HEAP_STRING_SUPER(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SUPER) #define DUK_HTHREAD_STRING_SUPER(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SUPER) -#define DUK_STRIDX_LC_NULL 179 /* 'null' */ +#define DUK_STRIDX_LC_NULL 155 /* 'null' */ #define DUK_HEAP_STRING_LC_NULL(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NULL) #define DUK_HTHREAD_STRING_LC_NULL(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NULL) -#define DUK_STRIDX_TRUE 180 /* 'true' */ +#define DUK_STRIDX_TRUE 156 /* 'true' */ #define DUK_HEAP_STRING_TRUE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRUE) #define DUK_HTHREAD_STRING_TRUE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRUE) -#define DUK_STRIDX_FALSE 181 /* 'false' */ +#define DUK_STRIDX_FALSE 157 /* 'false' */ #define DUK_HEAP_STRING_FALSE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FALSE) #define DUK_HTHREAD_STRING_FALSE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FALSE) -#define DUK_STRIDX_IMPLEMENTS 182 /* 'implements' */ +#define DUK_STRIDX_IMPLEMENTS 158 /* 'implements' */ #define DUK_HEAP_STRING_IMPLEMENTS(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPLEMENTS) #define DUK_HTHREAD_STRING_IMPLEMENTS(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPLEMENTS) -#define DUK_STRIDX_INTERFACE 183 /* 'interface' */ +#define DUK_STRIDX_INTERFACE 159 /* 'interface' */ #define DUK_HEAP_STRING_INTERFACE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INTERFACE) #define DUK_HTHREAD_STRING_INTERFACE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INTERFACE) -#define DUK_STRIDX_LET 184 /* 'let' */ +#define DUK_STRIDX_LET 160 /* 'let' */ #define DUK_HEAP_STRING_LET(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LET) #define DUK_HTHREAD_STRING_LET(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LET) -#define DUK_STRIDX_PACKAGE 185 /* 'package' */ +#define DUK_STRIDX_PACKAGE 161 /* 'package' */ #define DUK_HEAP_STRING_PACKAGE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PACKAGE) #define DUK_HTHREAD_STRING_PACKAGE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PACKAGE) -#define DUK_STRIDX_PRIVATE 186 /* 'private' */ +#define DUK_STRIDX_PRIVATE 162 /* 'private' */ #define DUK_HEAP_STRING_PRIVATE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PRIVATE) #define DUK_HTHREAD_STRING_PRIVATE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PRIVATE) -#define DUK_STRIDX_PROTECTED 187 /* 'protected' */ +#define DUK_STRIDX_PROTECTED 163 /* 'protected' */ #define DUK_HEAP_STRING_PROTECTED(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTECTED) #define DUK_HTHREAD_STRING_PROTECTED(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTECTED) -#define DUK_STRIDX_PUBLIC 188 /* 'public' */ +#define DUK_STRIDX_PUBLIC 164 /* 'public' */ #define DUK_HEAP_STRING_PUBLIC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PUBLIC) #define DUK_HTHREAD_STRING_PUBLIC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PUBLIC) -#define DUK_STRIDX_STATIC 189 /* 'static' */ +#define DUK_STRIDX_STATIC 165 /* 'static' */ #define DUK_HEAP_STRING_STATIC(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STATIC) #define DUK_HTHREAD_STRING_STATIC(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STATIC) -#define DUK_STRIDX_YIELD 190 /* 'yield' */ +#define DUK_STRIDX_YIELD 166 /* 'yield' */ #define DUK_HEAP_STRING_YIELD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_YIELD) #define DUK_HTHREAD_STRING_YIELD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_YIELD) -#define DUK_HEAP_NUM_STRINGS 191 -#define DUK_STRIDX_START_RESERVED 146 -#define DUK_STRIDX_START_STRICT_RESERVED 182 -#define DUK_STRIDX_END_RESERVED 191 /* exclusive endpoint */ +#define DUK_HEAP_NUM_STRINGS 167 +#define DUK_STRIDX_START_RESERVED 122 +#define DUK_STRIDX_START_STRICT_RESERVED 158 +#define DUK_STRIDX_END_RESERVED 167 /* exclusive endpoint */ /* To convert a heap stridx to a token number, subtract * DUK_STRIDX_START_RESERVED and add DUK_TOK_START_RESERVED. */ #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[1049]; +DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[972]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_STRDATA_MAX_STRLEN 17 -#define DUK_STRDATA_DATA_LENGTH 1049 +#define DUK_STRDATA_MAX_STRLEN 27 +#define DUK_STRDATA_DATA_LENGTH 972 #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_ROM_OBJECTS) -#error ROM support not enabled, rerun make_dist.py with --rom-support -#else +#error RAM support not enabled, rerun configure.py with --ram-support +#else /* DUK_USE_ROM_OBJECTS */ DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_context *ctx); @@ -1486,18 +2065,19 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_date_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_type_error_thrower(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_logger_constructor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_constructor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_eval(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx); @@ -1506,12 +2086,11 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_escape(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_global_object_require(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_assign(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx); @@ -1519,16 +2098,22 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_con DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_object_constructor_is(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_hasinstance(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_length(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_name(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx); @@ -1545,6 +2130,7 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *c DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_char_code(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_constructor_from_code_point(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_at(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx); @@ -1559,8 +2145,12 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_number_check_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_value_of(duk_context *ctx); @@ -1577,9 +2167,12 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_date_prototype_toprimitive(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_to_string(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_tostring(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx); @@ -1589,9 +2182,13 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_clz32(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_imul(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_max(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_min(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_random(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_sign(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_parse(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx); @@ -1604,16 +2201,26 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_compact(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_yield(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_resume(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_current(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_logger_prototype_fmt(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_logger_prototype_raw(duk_context *ctx); -DUK_INTERNAL_DECL duk_ret_t duk_bi_logger_prototype_log_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_apply(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_construct(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_delete_property(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_has(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_tostring_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_symbol_toprimitive(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_typedarray_set(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_allocplain(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx); @@ -1624,122 +2231,113 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_encode(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_cbor_decode(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_performance_now(duk_context *ctx); #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[149]; +DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[185]; #endif /* !DUK_SINGLE_FILE */ -#if defined(DUK_USE_BUILTIN_INITJS) -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_initjs_data[187]; -#endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTIN_INITJS_DATA_LENGTH 187 -#endif /* DUK_USE_BUILTIN_INITJS */ #define DUK_BIDX_GLOBAL 0 #define DUK_BIDX_GLOBAL_ENV 1 #define DUK_BIDX_OBJECT_CONSTRUCTOR 2 #define DUK_BIDX_OBJECT_PROTOTYPE 3 #define DUK_BIDX_FUNCTION_CONSTRUCTOR 4 #define DUK_BIDX_FUNCTION_PROTOTYPE 5 -#define DUK_BIDX_ARRAY_CONSTRUCTOR 6 -#define DUK_BIDX_ARRAY_PROTOTYPE 7 -#define DUK_BIDX_STRING_CONSTRUCTOR 8 -#define DUK_BIDX_STRING_PROTOTYPE 9 -#define DUK_BIDX_BOOLEAN_CONSTRUCTOR 10 -#define DUK_BIDX_BOOLEAN_PROTOTYPE 11 -#define DUK_BIDX_NUMBER_CONSTRUCTOR 12 -#define DUK_BIDX_NUMBER_PROTOTYPE 13 -#define DUK_BIDX_DATE_CONSTRUCTOR 14 -#define DUK_BIDX_DATE_PROTOTYPE 15 -#define DUK_BIDX_REGEXP_CONSTRUCTOR 16 -#define DUK_BIDX_REGEXP_PROTOTYPE 17 -#define DUK_BIDX_ERROR_CONSTRUCTOR 18 -#define DUK_BIDX_ERROR_PROTOTYPE 19 -#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR 20 -#define DUK_BIDX_EVAL_ERROR_PROTOTYPE 21 -#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR 22 -#define DUK_BIDX_RANGE_ERROR_PROTOTYPE 23 -#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR 24 -#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE 25 -#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR 26 -#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE 27 -#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR 28 -#define DUK_BIDX_TYPE_ERROR_PROTOTYPE 29 -#define DUK_BIDX_URI_ERROR_CONSTRUCTOR 30 -#define DUK_BIDX_URI_ERROR_PROTOTYPE 31 -#define DUK_BIDX_MATH 32 -#define DUK_BIDX_JSON 33 -#define DUK_BIDX_TYPE_ERROR_THROWER 34 -#define DUK_BIDX_PROXY_CONSTRUCTOR 35 -#define DUK_BIDX_DUKTAPE 36 -#define DUK_BIDX_THREAD_CONSTRUCTOR 37 -#define DUK_BIDX_THREAD_PROTOTYPE 38 -#define DUK_BIDX_BUFFER_CONSTRUCTOR 39 -#define DUK_BIDX_BUFFER_PROTOTYPE 40 -#define DUK_BIDX_POINTER_CONSTRUCTOR 41 -#define DUK_BIDX_POINTER_PROTOTYPE 42 -#define DUK_BIDX_LOGGER_CONSTRUCTOR 43 -#define DUK_BIDX_LOGGER_PROTOTYPE 44 -#define DUK_BIDX_DOUBLE_ERROR 45 -#define DUK_BIDX_ARRAYBUFFER_CONSTRUCTOR 46 -#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE 47 -#define DUK_BIDX_DATAVIEW_CONSTRUCTOR 48 -#define DUK_BIDX_DATAVIEW_PROTOTYPE 49 -#define DUK_BIDX_TYPEDARRAY_PROTOTYPE 50 -#define DUK_BIDX_INT8ARRAY_CONSTRUCTOR 51 -#define DUK_BIDX_INT8ARRAY_PROTOTYPE 52 -#define DUK_BIDX_UINT8ARRAY_CONSTRUCTOR 53 -#define DUK_BIDX_UINT8ARRAY_PROTOTYPE 54 -#define DUK_BIDX_UINT8CLAMPEDARRAY_CONSTRUCTOR 55 -#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE 56 -#define DUK_BIDX_INT16ARRAY_CONSTRUCTOR 57 -#define DUK_BIDX_INT16ARRAY_PROTOTYPE 58 -#define DUK_BIDX_UINT16ARRAY_CONSTRUCTOR 59 -#define DUK_BIDX_UINT16ARRAY_PROTOTYPE 60 -#define DUK_BIDX_INT32ARRAY_CONSTRUCTOR 61 -#define DUK_BIDX_INT32ARRAY_PROTOTYPE 62 -#define DUK_BIDX_UINT32ARRAY_CONSTRUCTOR 63 -#define DUK_BIDX_UINT32ARRAY_PROTOTYPE 64 -#define DUK_BIDX_FLOAT32ARRAY_CONSTRUCTOR 65 -#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE 66 -#define DUK_BIDX_FLOAT64ARRAY_CONSTRUCTOR 67 -#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE 68 -#define DUK_BIDX_NODEJS_BUFFER_CONSTRUCTOR 69 -#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE 70 -#define DUK_NUM_BUILTINS 71 -#define DUK_NUM_BIDX_BUILTINS 71 -#define DUK_NUM_ALL_BUILTINS 71 +#define DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE 6 +#define DUK_BIDX_ARRAY_CONSTRUCTOR 7 +#define DUK_BIDX_ARRAY_PROTOTYPE 8 +#define DUK_BIDX_STRING_CONSTRUCTOR 9 +#define DUK_BIDX_STRING_PROTOTYPE 10 +#define DUK_BIDX_BOOLEAN_CONSTRUCTOR 11 +#define DUK_BIDX_BOOLEAN_PROTOTYPE 12 +#define DUK_BIDX_NUMBER_CONSTRUCTOR 13 +#define DUK_BIDX_NUMBER_PROTOTYPE 14 +#define DUK_BIDX_DATE_CONSTRUCTOR 15 +#define DUK_BIDX_DATE_PROTOTYPE 16 +#define DUK_BIDX_REGEXP_CONSTRUCTOR 17 +#define DUK_BIDX_REGEXP_PROTOTYPE 18 +#define DUK_BIDX_ERROR_CONSTRUCTOR 19 +#define DUK_BIDX_ERROR_PROTOTYPE 20 +#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR 21 +#define DUK_BIDX_EVAL_ERROR_PROTOTYPE 22 +#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR 23 +#define DUK_BIDX_RANGE_ERROR_PROTOTYPE 24 +#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR 25 +#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE 26 +#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR 27 +#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE 28 +#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR 29 +#define DUK_BIDX_TYPE_ERROR_PROTOTYPE 30 +#define DUK_BIDX_URI_ERROR_CONSTRUCTOR 31 +#define DUK_BIDX_URI_ERROR_PROTOTYPE 32 +#define DUK_BIDX_TYPE_ERROR_THROWER 33 +#define DUK_BIDX_DUKTAPE 34 +#define DUK_BIDX_THREAD_PROTOTYPE 35 +#define DUK_BIDX_POINTER_PROTOTYPE 36 +#define DUK_BIDX_DOUBLE_ERROR 37 +#define DUK_BIDX_SYMBOL_PROTOTYPE 38 +#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE 39 +#define DUK_BIDX_DATAVIEW_PROTOTYPE 40 +#define DUK_BIDX_INT8ARRAY_PROTOTYPE 41 +#define DUK_BIDX_UINT8ARRAY_PROTOTYPE 42 +#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE 43 +#define DUK_BIDX_INT16ARRAY_PROTOTYPE 44 +#define DUK_BIDX_UINT16ARRAY_PROTOTYPE 45 +#define DUK_BIDX_INT32ARRAY_PROTOTYPE 46 +#define DUK_BIDX_UINT32ARRAY_PROTOTYPE 47 +#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE 48 +#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE 49 +#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE 50 +#define DUK_NUM_BUILTINS 51 +#define DUK_NUM_BIDX_BUILTINS 51 +#define DUK_NUM_ALL_BUILTINS 80 #if defined(DUK_USE_DOUBLE_LE) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3833]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3833 +#define DUK_BUILTINS_DATA_LENGTH 4281 #elif defined(DUK_USE_DOUBLE_BE) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3833]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3833 +#define DUK_BUILTINS_DATA_LENGTH 4281 #elif defined(DUK_USE_DOUBLE_ME) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3833]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[4281]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3833 +#define DUK_BUILTINS_DATA_LENGTH 4281 #else #error invalid endianness defines #endif #endif /* DUK_USE_ROM_OBJECTS */ #endif /* DUK_BUILTINS_H_INCLUDED */ -#line 52 "duk_internal.h" -#line 1 "duk_util.h" +/* #include duk_util.h */ /* * Utilities */ -#ifndef DUK_UTIL_H_INCLUDED +#if !defined(DUK_UTIL_H_INCLUDED) #define DUK_UTIL_H_INCLUDED -#define DUK_UTIL_MIN_HASH_PRIME 17 /* must match genhashsizes.py */ +#if defined(DUK_USE_GET_RANDOM_DOUBLE) +#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) DUK_USE_GET_RANDOM_DOUBLE((thr)->heap_udata) +#else +#define DUK_UTIL_GET_RANDOM_DOUBLE(thr) duk_util_tinyrandom_get_double(thr) +#endif -#define DUK_UTIL_GET_HASH_PROBE_STEP(hash) (duk_util_probe_steps[(hash) & 0x1f]) +/* + * Some useful constants + */ + +#define DUK_DOUBLE_2TO32 4294967296.0 +#define DUK_DOUBLE_2TO31 2147483648.0 +#define DUK_DOUBLE_LOG2E 1.4426950408889634 +#define DUK_DOUBLE_LOG10E 0.4342944819032518 /* * Endian conversion @@ -1771,6 +2369,8 @@ struct duk_bitdecoder_ctx { duk_small_int_t currbits; }; +#define DUK_BD_BITPACKED_STRING_MAXLEN 256 + /* * Bitstream encoder */ @@ -1786,48 +2386,57 @@ struct duk_bitencoder_ctx { /* * Raw write/read macros for big endian, unaligned basic values. - * Caller ensures there's enough space. The macros update the pointer - * argument automatically on resizes. The idiom seems a bit odd, but - * leads to compact code. + * Caller ensures there's enough space. The INC macro variants + * update the pointer argument automatically. */ #define DUK_RAW_WRITE_U8(ptr,val) do { \ + *(ptr) = (duk_uint8_t) (val); \ + } while (0) +#define DUK_RAW_WRITE_U16_BE(ptr,val) duk_raw_write_u16_be((ptr), (duk_uint16_t) (val)) +#define DUK_RAW_WRITE_U32_BE(ptr,val) duk_raw_write_u32_be((ptr), (duk_uint32_t) (val)) +#define DUK_RAW_WRITE_FLOAT_BE(ptr,val) duk_raw_write_float_be((ptr), (duk_float_t) (val)) +#define DUK_RAW_WRITE_DOUBLE_BE(ptr,val) duk_raw_write_double_be((ptr), (duk_double_t) (val)) +#define DUK_RAW_WRITE_XUTF8(ptr,val) duk_raw_write_xutf8((ptr), (duk_ucodepoint_t) (val)) + +#define DUK_RAW_WRITEINC_U8(ptr,val) do { \ *(ptr)++ = (duk_uint8_t) (val); \ } while (0) -#define DUK_RAW_WRITE_U16_BE(ptr,val) duk_raw_write_u16_be(&(ptr), (duk_uint16_t) (val)) -#define DUK_RAW_WRITE_U32_BE(ptr,val) duk_raw_write_u32_be(&(ptr), (duk_uint32_t) (val)) -#define DUK_RAW_WRITE_DOUBLE_BE(ptr,val) duk_raw_write_double_be(&(ptr), (duk_double_t) (val)) -#define DUK_RAW_WRITE_XUTF8(ptr,val) do { \ - /* 'ptr' is evaluated both as LHS and RHS. */ \ - duk_uint8_t *duk__ptr; \ - duk_small_int_t duk__len; \ - duk__ptr = (duk_uint8_t *) (ptr); \ - duk__len = duk_unicode_encode_xutf8((duk_ucodepoint_t) (val), duk__ptr); \ - duk__ptr += duk__len; \ - (ptr) = duk__ptr; \ - } while (0) -#define DUK_RAW_WRITE_CESU8(ptr,val) do { \ - /* 'ptr' is evaluated both as LHS and RHS. */ \ - duk_uint8_t *duk__ptr; \ - duk_small_int_t duk__len; \ - duk__ptr = (duk_uint8_t *) (ptr); \ - duk__len = duk_unicode_encode_cesu8((duk_ucodepoint_t) (val), duk__ptr); \ - duk__ptr += duk__len; \ - (ptr) = duk__ptr; \ - } while (0) +#define DUK_RAW_WRITEINC_U16_BE(ptr,val) duk_raw_writeinc_u16_be(&(ptr), (duk_uint16_t) (val)) +#define DUK_RAW_WRITEINC_U32_BE(ptr,val) duk_raw_writeinc_u32_be(&(ptr), (duk_uint32_t) (val)) +#define DUK_RAW_WRITEINC_FLOAT_BE(ptr,val) duk_raw_writeinc_float_be(&(ptr), (duk_float_t) (val)) +#define DUK_RAW_WRITEINC_DOUBLE_BE(ptr,val) duk_raw_writeinc_double_be(&(ptr), (duk_double_t) (val)) +#define DUK_RAW_WRITEINC_XUTF8(ptr,val) duk_raw_writeinc_xutf8(&(ptr), (duk_ucodepoint_t) (val)) +#define DUK_RAW_WRITEINC_CESU8(ptr,val) duk_raw_writeinc_cesu8(&(ptr), (duk_ucodepoint_t) (val)) -#define DUK_RAW_READ_U8(ptr) ((duk_uint8_t) (*(ptr)++)) -#define DUK_RAW_READ_U16_BE(ptr) duk_raw_read_u16_be(&(ptr)); -#define DUK_RAW_READ_U32_BE(ptr) duk_raw_read_u32_be(&(ptr)); -#define DUK_RAW_READ_DOUBLE_BE(ptr) duk_raw_read_double_be(&(ptr)); +#define DUK_RAW_READ_U8(ptr) ((duk_uint8_t) (*(ptr))) +#define DUK_RAW_READ_U16_BE(ptr) duk_raw_read_u16_be((ptr)); +#define DUK_RAW_READ_U32_BE(ptr) duk_raw_read_u32_be((ptr)); +#define DUK_RAW_READ_DOUBLE_BE(ptr) duk_raw_read_double_be((ptr)); + +#define DUK_RAW_READINC_U8(ptr) ((duk_uint8_t) (*(ptr)++)) +#define DUK_RAW_READINC_U16_BE(ptr) duk_raw_readinc_u16_be(&(ptr)); +#define DUK_RAW_READINC_U32_BE(ptr) duk_raw_readinc_u32_be(&(ptr)); +#define DUK_RAW_READINC_DOUBLE_BE(ptr) duk_raw_readinc_double_be(&(ptr)); + +/* + * Double and float byte order operations. + */ + +DUK_INTERNAL_DECL void duk_dblunion_host_to_little(duk_double_union *u); +DUK_INTERNAL_DECL void duk_dblunion_little_to_host(duk_double_union *u); +DUK_INTERNAL_DECL void duk_dblunion_host_to_big(duk_double_union *u); +DUK_INTERNAL_DECL void duk_dblunion_big_to_host(duk_double_union *u); +DUK_INTERNAL_DECL void duk_fltunion_host_to_big(duk_float_union *u); +DUK_INTERNAL_DECL void duk_fltunion_big_to_host(duk_float_union *u); /* * Buffer writer (dynamic buffer only) * - * Helper for writing to a dynamic buffer with a concept of a "spare" area + * Helper for writing to a dynamic buffer with a concept of a "slack" area * to reduce resizes. You can ensure there is enough space beforehand and * then write for a while without further checks, relying on a stable data - * pointer. Spare handling is automatic so call sites only indicate how + * pointer. Slack handling is automatic so call sites only indicate how * much data they need right now. * * There are several ways to write using bufwriter. The best approach @@ -1853,8 +2462,13 @@ struct duk_bufwriter_ctx { duk_hbuffer_dynamic *buf; }; -#define DUK_BW_SPARE_ADD 64 -#define DUK_BW_SPARE_SHIFT 4 /* 2^4 -> 1/16 = 6.25% spare */ +#if defined(DUK_USE_PREFER_SIZE) +#define DUK_BW_SLACK_ADD 64 +#define DUK_BW_SLACK_SHIFT 4 /* 2^4 -> 1/16 = 6.25% slack */ +#else +#define DUK_BW_SLACK_ADD 64 +#define DUK_BW_SLACK_SHIFT 2 /* 2^2 -> 1/4 = 25% slack */ +#endif /* Initialization and finalization (compaction), converting to other types. */ @@ -1869,28 +2483,24 @@ struct duk_bufwriter_ctx { duk_bw_compact((thr), (bw_ctx)); \ } while (0) #define DUK_BW_PUSH_AS_STRING(thr,bw_ctx) do { \ - duk_push_lstring((duk_context *) (thr), \ + duk_push_lstring((thr), \ (const char *) (bw_ctx)->p_base, \ (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \ } while (0) + /* Pointers may be NULL for a while when 'buf' size is zero and before any * ENSURE calls have been made. Once an ENSURE has been made, the pointers * are required to be non-NULL so that it's always valid to use memcpy() and * memmove(), even for zero size. */ -#define DUK_BW_ASSERT_VALID_EXPR(thr,bw_ctx) \ - DUK_ASSERT_EXPR((bw_ctx) != NULL && \ - (bw_ctx)->buf != NULL && \ - ((DUK_HBUFFER_DYNAMIC_GET_SIZE((bw_ctx)->buf) == 0) || \ - ((bw_ctx)->p != NULL && \ - (bw_ctx)->p_base != NULL && \ - (bw_ctx)->p_limit != NULL && \ - (bw_ctx)->p_limit >= (bw_ctx)->p_base && \ - (bw_ctx)->p >= (bw_ctx)->p_base && \ - (bw_ctx)->p <= (bw_ctx)->p_limit))) -#define DUK_BW_ASSERT_VALID(thr,bw_ctx) do { \ - DUK_BW_ASSERT_VALID_EXPR((thr), (bw_ctx)); \ - } while (0) +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_bw_assert_valid(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx); +#define DUK_BW_ASSERT_VALID_EXPR(thr,bw_ctx) (duk_bw_assert_valid((thr), (bw_ctx))) +#define DUK_BW_ASSERT_VALID(thr,bw_ctx) do { duk_bw_assert_valid((thr), (bw_ctx)); } while (0) +#else +#define DUK_BW_ASSERT_VALID_EXPR(thr,bw_ctx) DUK_ASSERT_EXPR(1) +#define DUK_BW_ASSERT_VALID(thr,bw_ctx) do {} while (0) +#endif /* Working with the pointer and current size. */ @@ -1952,7 +2562,7 @@ struct duk_bufwriter_ctx { duk_bw_compact((thr), (bw_ctx)); \ } while (0) -/* Fast write calls which assume you control the spare beforehand. +/* Fast write calls which assume you control the slack beforehand. * Multibyte write variants exist and use a temporary write pointer * because byte writes alias with anything: with a stored pointer * explicit pointer load/stores get generated (e.g. gcc -Os). @@ -2015,7 +2625,7 @@ struct duk_bufwriter_ctx { #define DUK_BW_WRITE_RAW_XUTF8(thr,bw_ctx,cp) do { \ duk_ucodepoint_t duk__cp; \ duk_small_int_t duk__enc_len; \ - duk__cp = (cp); \ + duk__cp = (duk_ucodepoint_t) (cp); \ DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_xutf8_length(duk__cp)); \ duk__enc_len = duk_unicode_encode_xutf8(duk__cp, (bw_ctx)->p); \ (bw_ctx)->p += duk__enc_len; \ @@ -2029,12 +2639,13 @@ struct duk_bufwriter_ctx { (bw_ctx)->p += duk__enc_len; \ } while (0) /* XXX: add temporary duk__p pointer here too; sharing */ +/* XXX: avoid unsafe variants */ #define DUK_BW_WRITE_RAW_BYTES(thr,bw_ctx,valptr,valsz) do { \ const void *duk__valptr; \ duk_size_t duk__valsz; \ duk__valptr = (const void *) (valptr); \ duk__valsz = (duk_size_t) (valsz); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ (bw_ctx)->p += duk__valsz; \ } while (0) #define DUK_BW_WRITE_RAW_CSTRING(thr,bw_ctx,val) do { \ @@ -2042,31 +2653,31 @@ struct duk_bufwriter_ctx { duk_size_t duk__val_len; \ duk__val = (const duk_uint8_t *) (val); \ duk__val_len = DUK_STRLEN((const char *) duk__val); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HSTRING(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER_FIXED(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_RAW_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) @@ -2130,13 +2741,14 @@ struct duk_bufwriter_ctx { DUK_BW_WRITE_RAW_CESU8((thr), (bw_ctx), (cp)); \ } while (0) /* XXX: add temporary duk__p pointer here too; sharing */ +/* XXX: avoid unsafe */ #define DUK_BW_WRITE_ENSURE_BYTES(thr,bw_ctx,valptr,valsz) do { \ const void *duk__valptr; \ duk_size_t duk__valsz; \ duk__valptr = (const void *) (valptr); \ duk__valsz = (duk_size_t) (valsz); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__valsz); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), duk__valptr, duk__valsz); \ (bw_ctx)->p += duk__valsz; \ } while (0) #define DUK_BW_WRITE_ENSURE_CSTRING(thr,bw_ctx,val) do { \ @@ -2145,35 +2757,35 @@ struct duk_bufwriter_ctx { duk__val = (const duk_uint8_t *) (val); \ duk__val_len = DUK_STRLEN((const char *) duk__val); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) duk__val, duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HSTRING(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HSTRING_GET_BYTELEN((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HSTRING_GET_DATA((val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER_FIXED(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_FIXED_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_FIXED_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) #define DUK_BW_WRITE_ENSURE_HBUFFER_DYNAMIC(thr,bw_ctx,val) do { \ duk_size_t duk__val_len; \ duk__val_len = DUK_HBUFFER_DYNAMIC_GET_SIZE((val)); \ DUK_BW_ENSURE((thr), (bw_ctx), duk__val_len); \ - DUK_MEMCPY((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ + duk_memcpy_unsafe((void *) ((bw_ctx)->p), (const void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((thr)->heap, (val)), duk__val_len); \ (bw_ctx)->p += duk__val_len; \ } while (0) @@ -2202,14 +2814,10 @@ DUK_INTERNAL_DECL const duk_int8_t duk_hex_dectab[256]; DUK_INTERNAL_DECL const duk_int16_t duk_hex_dectab_shift4[256]; DUK_INTERNAL_DECL const duk_uint16_t duk_hex_enctab[256]; #endif -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_INTERNAL_DECL const duk_uint8_t duk_base64_enctab[64]; -DUK_INTERNAL_DECL const duk_int8_t duk_base64_dectab[256]; -#endif #endif /* !DUK_SINGLE_FILE */ /* Note: assumes that duk_util_probe_steps size is 32 */ -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) +#if defined(DUK_USE_HOBJECT_HASH_PART) #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32]; #endif /* !DUK_SINGLE_FILE */ @@ -2219,19 +2827,20 @@ DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32]; DUK_INTERNAL_DECL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed); #endif -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) -DUK_INTERNAL_DECL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size); -#endif - -DUK_INTERNAL_DECL duk_int32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits); -DUK_INTERNAL_DECL duk_small_int_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx); -DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value); +DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits); +DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx); +DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value); +DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value); +DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx); +DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out); DUK_INTERNAL_DECL void duk_be_encode(duk_bitencoder_ctx *ctx, duk_uint32_t data, duk_small_int_t bits); DUK_INTERNAL_DECL void duk_be_finish(duk_bitencoder_ctx *ctx); -DUK_INTERNAL_DECL duk_uint32_t duk_util_tinyrandom_get_bits(duk_hthread *thr, duk_small_int_t n); +#if !defined(DUK_USE_GET_RANDOM_DOUBLE) DUK_INTERNAL_DECL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_util_tinyrandom_prepare_seed(duk_hthread *thr); +#endif DUK_INTERNAL_DECL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_hbuffer_dynamic *h_buf); DUK_INTERNAL_DECL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size); @@ -2248,483 +2857,856 @@ DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_b DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len); /* No duk_bw_remove_ensure_slice(), functionality would be identical. */ -DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p); -DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p); -DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(duk_uint8_t **p); -DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val); -DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val); -DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val); +DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p); +DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p); +DUK_INTERNAL_DECL duk_float_t duk_raw_read_float_be(const duk_uint8_t *p); +DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(const duk_uint8_t *p); +DUK_INTERNAL_DECL duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p); +DUK_INTERNAL_DECL duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p); +DUK_INTERNAL_DECL duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p); +DUK_INTERNAL_DECL duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p); +DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val); +DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val); +DUK_INTERNAL_DECL void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val); +DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val); +DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val); +DUK_INTERNAL_DECL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val); +DUK_INTERNAL_DECL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val); #if defined(DUK_USE_DEBUGGER_SUPPORT) /* For now only needed by the debugger. */ -DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len); +DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len); #endif -#endif /* DUK_UTIL_H_INCLUDED */ -#line 1 "duk_strings.h" +/* memcpy(), memmove() etc wrappers. The plain variants like duk_memcpy() + * assume C99+ and 'src' and 'dst' pointers must be non-NULL even when the + * operation size is zero. The unsafe variants like duk_memcpy_safe() deal + * with the zero size case explicitly, and allow NULL pointers in that case + * (which is undefined behavior in C99+). For the majority of actual targets + * a NULL pointer with a zero length is fine in practice. These wrappers are + * macros to force inlining; because there are hundreds of call sites, even a + * few extra bytes per call site adds up to ~1kB footprint. + */ +#if defined(DUK_USE_ALLOW_UNDEFINED_BEHAVIOR) +#define duk_memcpy(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memcpy_unsafe(dst,src,len) duk_memcpy((dst), (src), (len)) +#define duk_memmove(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memmove_unsafe(dst,src,len) duk_memmove((dst), (src), (len)) +#define duk_memset(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } while (0) +#define duk_memset_unsafe(dst,val,len) duk_memset((dst), (val), (len)) +#define duk_memzero(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } while (0) +#define duk_memzero_unsafe(dst,len) duk_memzero((dst), (len)) +#else /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ +#define duk_memcpy(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memcpy_unsafe(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMCPY(duk__dst, duk__src, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memmove(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } while (0) +#define duk_memmove_unsafe(dst,src,len) do { \ + void *duk__dst = (dst); \ + const void *duk__src = (src); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + DUK_ASSERT(duk__src != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + DUK_ASSERT(duk__src != NULL); \ + (void) DUK_MEMMOVE(duk__dst, duk__src, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memset(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } while (0) +#define duk_memset_unsafe(dst,val,len) do { \ + void *duk__dst = (dst); \ + duk_small_int_t duk__val = (val); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMSET(duk__dst, duk__val, (size_t) duk__len); \ + } \ + } while (0) +#define duk_memzero(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } while (0) +#define duk_memzero_unsafe(dst,len) do { \ + void *duk__dst = (dst); \ + duk_size_t duk__len = (len); \ + DUK_ASSERT(duk__dst != NULL || duk__len == 0U); \ + if (DUK_LIKELY(duk__len > 0U)) { \ + DUK_ASSERT(duk__dst != NULL); \ + (void) DUK_MEMZERO(duk__dst, (size_t) duk__len); \ + } \ + } while (0) +#endif /* DUK_USE_ALLOW_UNDEFINED_BEHAVIOR */ + +DUK_INTERNAL_DECL duk_small_int_t duk_memcmp(const void *s1, const void *s2, duk_size_t len); +DUK_INTERNAL_DECL duk_small_int_t duk_memcmp_unsafe(const void *s1, const void *s2, duk_size_t len); + +DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32_nonegzero(duk_double_t x, duk_int32_t *ival); +DUK_INTERNAL_DECL duk_bool_t duk_is_whole_get_int32(duk_double_t x, duk_int32_t *ival); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_anyinf(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_posinf(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_neginf(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_zero(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x); +DUK_INTERNAL_DECL duk_small_uint_t duk_double_signbit(duk_double_t x); +DUK_INTERNAL_DECL duk_double_t duk_double_trunc_towards_zero(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_same_sign(duk_double_t x, duk_double_t y); +DUK_INTERNAL_DECL duk_double_t duk_double_fmin(duk_double_t x, duk_double_t y); +DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_finite(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_integer(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_is_safe_integer(duk_double_t x); + +DUK_INTERNAL_DECL duk_double_t duk_double_div(duk_double_t x, duk_double_t y); +DUK_INTERNAL_DECL duk_int_t duk_double_to_int_t(duk_double_t x); +DUK_INTERNAL_DECL duk_uint_t duk_double_to_uint_t(duk_double_t x); +DUK_INTERNAL_DECL duk_int32_t duk_double_to_int32_t(duk_double_t x); +DUK_INTERNAL_DECL duk_uint32_t duk_double_to_uint32_t(duk_double_t x); +DUK_INTERNAL_DECL duk_float_t duk_double_to_float_t(duk_double_t x); +DUK_INTERNAL_DECL duk_bool_t duk_double_equals(duk_double_t x, duk_double_t y); +DUK_INTERNAL_DECL duk_bool_t duk_float_equals(duk_float_t x, duk_float_t y); + /* - * Shared error messages: declarations and macros + * Miscellaneous + */ + +/* Example: x = 0x10 = 0b00010000 + * x - 1 = 0x0f = 0b00001111 + * x & (x - 1) == 0 * - * Error messages are accessed through macros with fine-grained, explicit - * error message distinctions. Concrete error messages are selected by the - * macros and multiple macros can map to the same concrete string to save - * on code footprint. This allows flexible footprint/verbosity tuning with - * minimal code impact. There are a few limitations to this approach: - * (1) switching between plain messages and format strings doesn't work - * conveniently, and (2) conditional strings are a bit awkward to handle. + * x = 0x07 = 0b00000111 + * x - 1 = 0x06 = 0b00000110 + * x & (x - 1) != 0 + * + * However, incorrectly true for x == 0 so check for that explicitly. + */ +#define DUK_IS_POWER_OF_TWO(x) \ + ((x) != 0U && ((x) & ((x) - 1U)) == 0U) + +#endif /* DUK_UTIL_H_INCLUDED */ +/* #include duk_strings.h */ +/* + * Shared string macros. + * + * Using shared macros helps minimize strings data size because it's easy + * to check if an existing string could be used. String constants don't + * need to be all defined here; defining a string here makes sense if there's + * a high chance the string could be reused. Also, using macros allows + * a call site express the exact string needed, but the macro may map to an + * approximate string to reduce unique string count. Macros can also be + * more easily tuned for low memory targets than #if defined()s throughout + * the code base. * * Because format strings behave differently in the call site (they need to - * be followed by format arguments), they have a special prefix (DUK_STR_FMT_ - * and duk_str_fmt_). + * be followed by format arguments), they use a special prefix DUK_STR_FMT_. * * On some compilers using explicit shared strings is preferable; on others * it may be better to use straight literals because the compiler will combine * them anyway, and such strings won't end up unnecessarily in a symbol table. */ -#ifndef DUK_ERRMSG_H_INCLUDED +#if !defined(DUK_ERRMSG_H_INCLUDED) #define DUK_ERRMSG_H_INCLUDED -#define DUK_STR_INTERNAL_ERROR duk_str_internal_error -#define DUK_STR_INVALID_COUNT duk_str_invalid_count -#define DUK_STR_INVALID_CALL_ARGS duk_str_invalid_call_args -#define DUK_STR_NOT_CONSTRUCTABLE duk_str_not_constructable -#define DUK_STR_NOT_CALLABLE duk_str_not_callable -#define DUK_STR_NOT_EXTENSIBLE duk_str_not_extensible -#define DUK_STR_NOT_WRITABLE duk_str_not_writable -#define DUK_STR_NOT_CONFIGURABLE duk_str_not_configurable +/* Mostly API and built-in method related */ +#define DUK_STR_INTERNAL_ERROR "internal error" +#define DUK_STR_UNSUPPORTED "unsupported" +#define DUK_STR_INVALID_COUNT "invalid count" +#define DUK_STR_INVALID_ARGS "invalid args" +#define DUK_STR_INVALID_STATE "invalid state" +#define DUK_STR_INVALID_INPUT "invalid input" +#define DUK_STR_INVALID_LENGTH "invalid length" +#define DUK_STR_NOT_CONSTRUCTABLE "not constructable" +#define DUK_STR_CONSTRUCT_ONLY "constructor requires 'new'" +#define DUK_STR_NOT_CALLABLE "not callable" +#define DUK_STR_NOT_EXTENSIBLE "not extensible" +#define DUK_STR_NOT_WRITABLE "not writable" +#define DUK_STR_NOT_CONFIGURABLE "not configurable" +#define DUK_STR_INVALID_CONTEXT "invalid context" +#define DUK_STR_INVALID_INDEX "invalid args" +#define DUK_STR_PUSH_BEYOND_ALLOC_STACK "cannot push beyond allocated stack" +#define DUK_STR_NOT_UNDEFINED "unexpected type" +#define DUK_STR_NOT_NULL "unexpected type" +#define DUK_STR_NOT_BOOLEAN "unexpected type" +#define DUK_STR_NOT_NUMBER "unexpected type" +#define DUK_STR_NOT_STRING "unexpected type" +#define DUK_STR_NOT_OBJECT "unexpected type" +#define DUK_STR_NOT_POINTER "unexpected type" +#define DUK_STR_NOT_BUFFER "not buffer" /* still in use with verbose messages */ +#define DUK_STR_UNEXPECTED_TYPE "unexpected type" +#define DUK_STR_NOT_THREAD "unexpected type" +#define DUK_STR_NOT_COMPFUNC "unexpected type" +#define DUK_STR_NOT_NATFUNC "unexpected type" +#define DUK_STR_NOT_C_FUNCTION "unexpected type" +#define DUK_STR_NOT_FUNCTION "unexpected type" +#define DUK_STR_NOT_REGEXP "unexpected type" +#define DUK_STR_TOPRIMITIVE_FAILED "coercion to primitive failed" +#define DUK_STR_NUMBER_OUTSIDE_RANGE "number outside range" +#define DUK_STR_NOT_OBJECT_COERCIBLE "not object coercible" +#define DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL "cannot number coerce Symbol" +#define DUK_STR_CANNOT_STRING_COERCE_SYMBOL "cannot string coerce Symbol" +#define DUK_STR_STRING_TOO_LONG "string too long" +#define DUK_STR_BUFFER_TOO_LONG "buffer too long" +#define DUK_STR_ALLOC_FAILED "alloc failed" +#define DUK_STR_WRONG_BUFFER_TYPE "wrong buffer type" +#define DUK_STR_BASE64_ENCODE_FAILED "base64 encode failed" +#define DUK_STR_SOURCE_DECODE_FAILED "source decode failed" +#define DUK_STR_UTF8_DECODE_FAILED "utf-8 decode failed" +#define DUK_STR_BASE64_DECODE_FAILED "base64 decode failed" +#define DUK_STR_HEX_DECODE_FAILED "hex decode failed" +#define DUK_STR_INVALID_BYTECODE "invalid bytecode" +#define DUK_STR_NO_SOURCECODE "no sourcecode" +#define DUK_STR_RESULT_TOO_LONG "result too long" +#define DUK_STR_INVALID_CFUNC_RC "invalid C function rc" +#define DUK_STR_INVALID_INSTANCEOF_RVAL "invalid instanceof rval" +#define DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO "instanceof rval has no .prototype" -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_internal_error; -DUK_INTERNAL_DECL const char *duk_str_invalid_count; -DUK_INTERNAL_DECL const char *duk_str_invalid_call_args; -DUK_INTERNAL_DECL const char *duk_str_not_constructable; -DUK_INTERNAL_DECL const char *duk_str_not_callable; -DUK_INTERNAL_DECL const char *duk_str_not_extensible; -DUK_INTERNAL_DECL const char *duk_str_not_writable; -DUK_INTERNAL_DECL const char *duk_str_not_configurable; -#endif /* !DUK_SINGLE_FILE */ +/* JSON */ +#define DUK_STR_FMT_PTR "%p" +#define DUK_STR_FMT_INVALID_JSON "invalid json (at offset %ld)" +#define DUK_STR_CYCLIC_INPUT "cyclic input" -#define DUK_STR_INVALID_CONTEXT duk_str_invalid_context -#define DUK_STR_INVALID_INDEX duk_str_invalid_call_args -#define DUK_STR_PUSH_BEYOND_ALLOC_STACK duk_str_push_beyond_alloc_stack -#define DUK_STR_NOT_UNDEFINED duk_str_unexpected_type -#define DUK_STR_NOT_NULL duk_str_unexpected_type -#define DUK_STR_NOT_BOOLEAN duk_str_unexpected_type -#define DUK_STR_NOT_NUMBER duk_str_unexpected_type -#define DUK_STR_NOT_STRING duk_str_unexpected_type -#define DUK_STR_NOT_OBJECT duk_str_unexpected_type -#define DUK_STR_NOT_POINTER duk_str_unexpected_type -#define DUK_STR_NOT_BUFFER duk_str_not_buffer /* still in use with verbose messages */ -#define DUK_STR_UNEXPECTED_TYPE duk_str_unexpected_type -#define DUK_STR_NOT_THREAD duk_str_unexpected_type -#define DUK_STR_NOT_COMPILEDFUNCTION duk_str_unexpected_type -#define DUK_STR_NOT_NATIVEFUNCTION duk_str_unexpected_type -#define DUK_STR_NOT_C_FUNCTION duk_str_unexpected_type -#define DUK_STR_NOT_FUNCTION duk_str_unexpected_type -#define DUK_STR_NOT_REGEXP duk_str_unexpected_type -#define DUK_STR_DEFAULTVALUE_COERCE_FAILED duk_str_defaultvalue_coerce_failed -#define DUK_STR_NUMBER_OUTSIDE_RANGE duk_str_number_outside_range -#define DUK_STR_NOT_OBJECT_COERCIBLE duk_str_not_object_coercible -#define DUK_STR_STRING_TOO_LONG duk_str_string_too_long -#define DUK_STR_BUFFER_TOO_LONG duk_str_buffer_too_long -#define DUK_STR_SPRINTF_TOO_LONG duk_str_sprintf_too_long -#define DUK_STR_ALLOC_FAILED duk_str_alloc_failed -#define DUK_STR_POP_TOO_MANY duk_str_pop_too_many -#define DUK_STR_WRONG_BUFFER_TYPE duk_str_wrong_buffer_type -#define DUK_STR_ENCODE_FAILED duk_str_encode_failed -#define DUK_STR_DECODE_FAILED duk_str_decode_failed -#define DUK_STR_NO_SOURCECODE duk_str_no_sourcecode -#define DUK_STR_CONCAT_RESULT_TOO_LONG duk_str_concat_result_too_long -#define DUK_STR_UNIMPLEMENTED duk_str_unimplemented -#define DUK_STR_UNSUPPORTED duk_str_unsupported -#define DUK_STR_ARRAY_LENGTH_OVER_2G duk_str_array_length_over_2g +/* Generic codec */ +#define DUK_STR_DEC_RECLIMIT "decode recursion limit" +#define DUK_STR_ENC_RECLIMIT "encode recursion limit" -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_invalid_context; -DUK_INTERNAL_DECL const char *duk_str_push_beyond_alloc_stack; -DUK_INTERNAL_DECL const char *duk_str_not_buffer; -DUK_INTERNAL_DECL const char *duk_str_unexpected_type; -DUK_INTERNAL_DECL const char *duk_str_defaultvalue_coerce_failed; -DUK_INTERNAL_DECL const char *duk_str_number_outside_range; -DUK_INTERNAL_DECL const char *duk_str_not_object_coercible; -DUK_INTERNAL_DECL const char *duk_str_string_too_long; -DUK_INTERNAL_DECL const char *duk_str_buffer_too_long; -DUK_INTERNAL_DECL const char *duk_str_sprintf_too_long; -DUK_INTERNAL_DECL const char *duk_str_alloc_failed; -DUK_INTERNAL_DECL const char *duk_str_pop_too_many; -DUK_INTERNAL_DECL const char *duk_str_wrong_buffer_type; -DUK_INTERNAL_DECL const char *duk_str_encode_failed; -DUK_INTERNAL_DECL const char *duk_str_decode_failed; -DUK_INTERNAL_DECL const char *duk_str_no_sourcecode; -DUK_INTERNAL_DECL const char *duk_str_concat_result_too_long; -DUK_INTERNAL_DECL const char *duk_str_unimplemented; -DUK_INTERNAL_DECL const char *duk_str_unsupported; -DUK_INTERNAL_DECL const char *duk_str_array_length_over_2g; -#endif /* !DUK_SINGLE_FILE */ +/* Object property access */ +#define DUK_STR_INVALID_BASE "invalid base value" +#define DUK_STR_STRICT_CALLER_READ "cannot read strict 'caller'" +#define DUK_STR_PROXY_REJECTED "proxy rejected" +#define DUK_STR_INVALID_ARRAY_LENGTH "invalid array length" +#define DUK_STR_SETTER_UNDEFINED "setter undefined" +#define DUK_STR_INVALID_DESCRIPTOR "invalid descriptor" -#define DUK_STR_FMT_PTR duk_str_fmt_ptr -#define DUK_STR_FMT_INVALID_JSON duk_str_fmt_invalid_json -#define DUK_STR_JSONDEC_RECLIMIT duk_str_jsondec_reclimit -#define DUK_STR_JSONENC_RECLIMIT duk_str_jsonenc_reclimit -#define DUK_STR_CYCLIC_INPUT duk_str_cyclic_input +/* Proxy */ +#define DUK_STR_PROXY_REVOKED "proxy revoked" +#define DUK_STR_INVALID_TRAP_RESULT "invalid trap result" -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_fmt_ptr; -DUK_INTERNAL_DECL const char *duk_str_fmt_invalid_json; -DUK_INTERNAL_DECL const char *duk_str_jsondec_reclimit; -DUK_INTERNAL_DECL const char *duk_str_jsonenc_reclimit; -DUK_INTERNAL_DECL const char *duk_str_cyclic_input; -#endif /* !DUK_SINGLE_FILE */ +/* Variables */ -#define DUK_STR_PROXY_REVOKED duk_str_proxy_revoked -#define DUK_STR_INVALID_BASE duk_str_invalid_base -#define DUK_STR_STRICT_CALLER_READ duk_str_strict_caller_read -#define DUK_STR_PROXY_REJECTED duk_str_proxy_rejected -#define DUK_STR_INVALID_ARRAY_LENGTH duk_str_invalid_array_length -#define DUK_STR_ARRAY_LENGTH_WRITE_FAILED duk_str_array_length_write_failed -#define DUK_STR_ARRAY_LENGTH_NOT_WRITABLE duk_str_array_length_not_writable -#define DUK_STR_SETTER_UNDEFINED duk_str_setter_undefined -#define DUK_STR_REDEFINE_VIRT_PROP duk_str_redefine_virt_prop -#define DUK_STR_INVALID_DESCRIPTOR duk_str_invalid_descriptor -#define DUK_STR_PROPERTY_IS_VIRTUAL duk_str_property_is_virtual +/* Lexer */ +#define DUK_STR_INVALID_ESCAPE "invalid escape" +#define DUK_STR_UNTERMINATED_STRING "unterminated string" +#define DUK_STR_UNTERMINATED_COMMENT "unterminated comment" +#define DUK_STR_UNTERMINATED_REGEXP "unterminated regexp" +#define DUK_STR_TOKEN_LIMIT "token limit" +#define DUK_STR_REGEXP_SUPPORT_DISABLED "regexp support disabled" +#define DUK_STR_INVALID_NUMBER_LITERAL "invalid number literal" +#define DUK_STR_INVALID_TOKEN "invalid token" -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_proxy_revoked; -DUK_INTERNAL_DECL const char *duk_str_invalid_base; -DUK_INTERNAL_DECL const char *duk_str_strict_caller_read; -DUK_INTERNAL_DECL const char *duk_str_proxy_rejected; -DUK_INTERNAL_DECL const char *duk_str_invalid_array_length; -DUK_INTERNAL_DECL const char *duk_str_array_length_write_failed; -DUK_INTERNAL_DECL const char *duk_str_array_length_not_writable; -DUK_INTERNAL_DECL const char *duk_str_setter_undefined; -DUK_INTERNAL_DECL const char *duk_str_redefine_virt_prop; -DUK_INTERNAL_DECL const char *duk_str_invalid_descriptor; -DUK_INTERNAL_DECL const char *duk_str_property_is_virtual; -#endif /* !DUK_SINGLE_FILE */ +/* Compiler */ +#define DUK_STR_PARSE_ERROR "parse error" +#define DUK_STR_DUPLICATE_LABEL "duplicate label" +#define DUK_STR_INVALID_LABEL "invalid label" +#define DUK_STR_INVALID_ARRAY_LITERAL "invalid array literal" +#define DUK_STR_INVALID_OBJECT_LITERAL "invalid object literal" +#define DUK_STR_INVALID_VAR_DECLARATION "invalid variable declaration" +#define DUK_STR_CANNOT_DELETE_IDENTIFIER "cannot delete identifier" +#define DUK_STR_INVALID_EXPRESSION "invalid expression" +#define DUK_STR_INVALID_LVALUE "invalid lvalue" +#define DUK_STR_INVALID_NEWTARGET "invalid new.target" +#define DUK_STR_EXPECTED_IDENTIFIER "expected identifier" +#define DUK_STR_EMPTY_EXPR_NOT_ALLOWED "empty expression not allowed" +#define DUK_STR_INVALID_FOR "invalid for statement" +#define DUK_STR_INVALID_SWITCH "invalid switch statement" +#define DUK_STR_INVALID_BREAK_CONT_LABEL "invalid break/continue label" +#define DUK_STR_INVALID_RETURN "invalid return" +#define DUK_STR_INVALID_TRY "invalid try" +#define DUK_STR_INVALID_THROW "invalid throw" +#define DUK_STR_WITH_IN_STRICT_MODE "with in strict mode" +#define DUK_STR_FUNC_STMT_NOT_ALLOWED "function statement not allowed" +#define DUK_STR_UNTERMINATED_STMT "unterminated statement" +#define DUK_STR_INVALID_ARG_NAME "invalid argument name" +#define DUK_STR_INVALID_FUNC_NAME "invalid function name" +#define DUK_STR_INVALID_GETSET_NAME "invalid getter/setter name" +#define DUK_STR_FUNC_NAME_REQUIRED "function name required" -#define DUK_STR_PARSE_ERROR duk_str_parse_error -#define DUK_STR_DUPLICATE_LABEL duk_str_duplicate_label -#define DUK_STR_INVALID_LABEL duk_str_invalid_label -#define DUK_STR_INVALID_ARRAY_LITERAL duk_str_invalid_array_literal -#define DUK_STR_INVALID_OBJECT_LITERAL duk_str_invalid_object_literal -#define DUK_STR_INVALID_VAR_DECLARATION duk_str_invalid_var_declaration -#define DUK_STR_CANNOT_DELETE_IDENTIFIER duk_str_cannot_delete_identifier -#define DUK_STR_INVALID_EXPRESSION duk_str_invalid_expression -#define DUK_STR_INVALID_LVALUE duk_str_invalid_lvalue -#define DUK_STR_EXPECTED_IDENTIFIER duk_str_expected_identifier -#define DUK_STR_EMPTY_EXPR_NOT_ALLOWED duk_str_empty_expr_not_allowed -#define DUK_STR_INVALID_FOR duk_str_invalid_for -#define DUK_STR_INVALID_SWITCH duk_str_invalid_switch -#define DUK_STR_INVALID_BREAK_CONT_LABEL duk_str_invalid_break_cont_label -#define DUK_STR_INVALID_RETURN duk_str_invalid_return -#define DUK_STR_INVALID_TRY duk_str_invalid_try -#define DUK_STR_INVALID_THROW duk_str_invalid_throw -#define DUK_STR_WITH_IN_STRICT_MODE duk_str_with_in_strict_mode -#define DUK_STR_FUNC_STMT_NOT_ALLOWED duk_str_func_stmt_not_allowed -#define DUK_STR_UNTERMINATED_STMT duk_str_unterminated_stmt -#define DUK_STR_INVALID_ARG_NAME duk_str_invalid_arg_name -#define DUK_STR_INVALID_FUNC_NAME duk_str_invalid_func_name -#define DUK_STR_INVALID_GETSET_NAME duk_str_invalid_getset_name -#define DUK_STR_FUNC_NAME_REQUIRED duk_str_func_name_required +/* RegExp */ +#define DUK_STR_INVALID_QUANTIFIER "invalid regexp quantifier" +#define DUK_STR_INVALID_QUANTIFIER_NO_ATOM "quantifier without preceding atom" +#define DUK_STR_INVALID_QUANTIFIER_VALUES "quantifier values invalid (qmin > qmax)" +#define DUK_STR_QUANTIFIER_TOO_MANY_COPIES "quantifier requires too many atom copies" +#define DUK_STR_UNEXPECTED_CLOSING_PAREN "unexpected closing parenthesis" +#define DUK_STR_UNEXPECTED_END_OF_PATTERN "unexpected end of pattern" +#define DUK_STR_UNEXPECTED_REGEXP_TOKEN "unexpected token in regexp" +#define DUK_STR_INVALID_REGEXP_FLAGS "invalid regexp flags" +#define DUK_STR_INVALID_REGEXP_ESCAPE "invalid regexp escape" +#define DUK_STR_INVALID_BACKREFS "invalid backreference(s)" +#define DUK_STR_INVALID_REGEXP_CHARACTER "invalid regexp character" +#define DUK_STR_INVALID_REGEXP_GROUP "invalid regexp group" +#define DUK_STR_UNTERMINATED_CHARCLASS "unterminated character class" +#define DUK_STR_INVALID_RANGE "invalid range" -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_parse_error; -DUK_INTERNAL_DECL const char *duk_str_duplicate_label; -DUK_INTERNAL_DECL const char *duk_str_invalid_label; -DUK_INTERNAL_DECL const char *duk_str_invalid_array_literal; -DUK_INTERNAL_DECL const char *duk_str_invalid_object_literal; -DUK_INTERNAL_DECL const char *duk_str_invalid_var_declaration; -DUK_INTERNAL_DECL const char *duk_str_cannot_delete_identifier; -DUK_INTERNAL_DECL const char *duk_str_invalid_expression; -DUK_INTERNAL_DECL const char *duk_str_invalid_lvalue; -DUK_INTERNAL_DECL const char *duk_str_expected_identifier; -DUK_INTERNAL_DECL const char *duk_str_empty_expr_not_allowed; -DUK_INTERNAL_DECL const char *duk_str_invalid_for; -DUK_INTERNAL_DECL const char *duk_str_invalid_switch; -DUK_INTERNAL_DECL const char *duk_str_invalid_break_cont_label; -DUK_INTERNAL_DECL const char *duk_str_invalid_return; -DUK_INTERNAL_DECL const char *duk_str_invalid_try; -DUK_INTERNAL_DECL const char *duk_str_invalid_throw; -DUK_INTERNAL_DECL const char *duk_str_with_in_strict_mode; -DUK_INTERNAL_DECL const char *duk_str_func_stmt_not_allowed; -DUK_INTERNAL_DECL const char *duk_str_unterminated_stmt; -DUK_INTERNAL_DECL const char *duk_str_invalid_arg_name; -DUK_INTERNAL_DECL const char *duk_str_invalid_func_name; -DUK_INTERNAL_DECL const char *duk_str_invalid_getset_name; -DUK_INTERNAL_DECL const char *duk_str_func_name_required; -#endif /* !DUK_SINGLE_FILE */ - -#define DUK_STR_INVALID_QUANTIFIER_NO_ATOM duk_str_invalid_quantifier_no_atom -#define DUK_STR_INVALID_QUANTIFIER_VALUES duk_str_invalid_quantifier_values -#define DUK_STR_QUANTIFIER_TOO_MANY_COPIES duk_str_quantifier_too_many_copies -#define DUK_STR_UNEXPECTED_CLOSING_PAREN duk_str_unexpected_closing_paren -#define DUK_STR_UNEXPECTED_END_OF_PATTERN duk_str_unexpected_end_of_pattern -#define DUK_STR_UNEXPECTED_REGEXP_TOKEN duk_str_unexpected_regexp_token -#define DUK_STR_INVALID_REGEXP_FLAGS duk_str_invalid_regexp_flags -#define DUK_STR_INVALID_BACKREFS duk_str_invalid_backrefs - -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_invalid_quantifier_no_atom; -DUK_INTERNAL_DECL const char *duk_str_invalid_quantifier_values; -DUK_INTERNAL_DECL const char *duk_str_quantifier_too_many_copies; -DUK_INTERNAL_DECL const char *duk_str_unexpected_closing_paren; -DUK_INTERNAL_DECL const char *duk_str_unexpected_end_of_pattern; -DUK_INTERNAL_DECL const char *duk_str_unexpected_regexp_token; -DUK_INTERNAL_DECL const char *duk_str_invalid_regexp_flags; -DUK_INTERNAL_DECL const char *duk_str_invalid_backrefs; -#endif /* !DUK_SINGLE_FILE */ - -#define DUK_STR_VALSTACK_LIMIT duk_str_valstack_limit -#define DUK_STR_CALLSTACK_LIMIT duk_str_callstack_limit -#define DUK_STR_CATCHSTACK_LIMIT duk_str_catchstack_limit -#define DUK_STR_PROTOTYPE_CHAIN_LIMIT duk_str_prototype_chain_limit -#define DUK_STR_BOUND_CHAIN_LIMIT duk_str_bound_chain_limit -#define DUK_STR_C_CALLSTACK_LIMIT duk_str_c_callstack_limit -#define DUK_STR_COMPILER_RECURSION_LIMIT duk_str_compiler_recursion_limit -#define DUK_STR_BYTECODE_LIMIT duk_str_bytecode_limit -#define DUK_STR_REG_LIMIT duk_str_reg_limit -#define DUK_STR_TEMP_LIMIT duk_str_temp_limit -#define DUK_STR_CONST_LIMIT duk_str_const_limit -#define DUK_STR_FUNC_LIMIT duk_str_func_limit -#define DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT duk_str_regexp_compiler_recursion_limit -#define DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT duk_str_regexp_executor_recursion_limit -#define DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT duk_str_regexp_executor_step_limit - -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_valstack_limit; -DUK_INTERNAL_DECL const char *duk_str_callstack_limit; -DUK_INTERNAL_DECL const char *duk_str_catchstack_limit; -DUK_INTERNAL_DECL const char *duk_str_prototype_chain_limit; -DUK_INTERNAL_DECL const char *duk_str_bound_chain_limit; -DUK_INTERNAL_DECL const char *duk_str_c_callstack_limit; -DUK_INTERNAL_DECL const char *duk_str_compiler_recursion_limit; -DUK_INTERNAL_DECL const char *duk_str_bytecode_limit; -DUK_INTERNAL_DECL const char *duk_str_reg_limit; -DUK_INTERNAL_DECL const char *duk_str_temp_limit; -DUK_INTERNAL_DECL const char *duk_str_const_limit; -DUK_INTERNAL_DECL const char *duk_str_func_limit; -DUK_INTERNAL_DECL const char *duk_str_regexp_compiler_recursion_limit; -DUK_INTERNAL_DECL const char *duk_str_regexp_executor_recursion_limit; -DUK_INTERNAL_DECL const char *duk_str_regexp_executor_step_limit; -#endif /* !DUK_SINGLE_FILE */ - -#if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const char *duk_str_anon; -#endif /* !DUK_SINGLE_FILE */ +/* Limits */ +#define DUK_STR_VALSTACK_LIMIT "valstack limit" +#define DUK_STR_CALLSTACK_LIMIT "callstack limit" +#define DUK_STR_PROTOTYPE_CHAIN_LIMIT "prototype chain limit" +#define DUK_STR_BOUND_CHAIN_LIMIT "function call bound chain limit" +#define DUK_STR_NATIVE_STACK_LIMIT "C stack depth limit" +#define DUK_STR_COMPILER_RECURSION_LIMIT "compiler recursion limit" +#define DUK_STR_BYTECODE_LIMIT "bytecode limit" +#define DUK_STR_REG_LIMIT "register limit" +#define DUK_STR_TEMP_LIMIT "temp limit" +#define DUK_STR_CONST_LIMIT "const limit" +#define DUK_STR_FUNC_LIMIT "function limit" +#define DUK_STR_REGEXP_COMPILER_RECURSION_LIMIT "regexp compiler recursion limit" +#define DUK_STR_REGEXP_EXECUTOR_RECURSION_LIMIT "regexp executor recursion limit" +#define DUK_STR_REGEXP_EXECUTOR_STEP_LIMIT "regexp step limit" #endif /* DUK_ERRMSG_H_INCLUDED */ -#line 1 "duk_js_bytecode.h" +/* #include duk_js_bytecode.h */ /* - * Ecmascript bytecode + * ECMAScript bytecode */ -#ifndef DUK_JS_BYTECODE_H_INCLUDED +#if !defined(DUK_JS_BYTECODE_H_INCLUDED) #define DUK_JS_BYTECODE_H_INCLUDED /* - * Logical instruction layout - * ========================== + * Bytecode instruction layout + * =========================== + * + * Instructions are unsigned 32-bit integers divided as follows: * * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! - * +---------------------------------------------------+-----------+ - * ! C ! B ! A ! OP ! - * +---------------------------------------------------+-----------+ + * +-----------------------------------------------+---------------+ + * ! C ! B ! A ! OP ! + * +-----------------------------------------------+---------------+ * - * OP (6 bits): opcode (DUK_OP_*), access should be fastest - * A (8 bits): typically a target register number - * B (9 bits): typically first source register/constant number - * C (9 bits): typically second source register/constant number + * OP (8 bits): opcode (DUK_OP_*), access should be fastest + * consecutive opcodes allocated when opcode needs flags + * A (8 bits): typically a target register number + * B (8 bits): typically first source register/constant number + * C (8 bits): typically second source register/constant number * * Some instructions combine BC or ABC together for larger parameter values. - * Signed integers (e.g. jump offsets) are encoded as unsigned, with an opcode - * specific bias. B and C may denote a register or a constant, see - * DUK_BC_ISREG() and DUK_BC_ISCONST(). + * Signed integers (e.g. jump offsets) are encoded as unsigned, with an + * opcode specific bias. * - * Note: macro naming is a bit misleading, e.g. "ABC" in macro name but - * the field layout is logically "CBA". + * Some opcodes have flags which are handled by allocating consecutive + * opcodes to make space for 1-N flags. Flags can also be e.g. in the 'A' + * field when there's room for the specific opcode. + * + * For example, if three flags were needed, they could be allocated from + * the opcode field as follows: + * + * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! + * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! + * +-----------------------------------------------+---------------+ + * ! C ! B ! A ! OP !Z!Y!X! + * +-----------------------------------------------+---------------+ + * + * Some opcodes accept a reg/const argument which is handled by allocating + * flags in the OP field, see DUK_BC_ISREG() and DUK_BC_ISCONST(). The + * following convention is shared by most opcodes, so that the compiler + * can handle reg/const flagging without opcode specific code paths: + * + * !3!3!2!2!2!2!2!2!2!2!2!2!1!1!1!1!1!1!1!1!1!1! ! ! ! ! ! ! ! ! ! ! + * !1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0!9!8!7!6!5!4!3!2!1!0! + * +-----------------------------------------------+---------------+ + * ! C ! B ! A ! OP !Y!X! + * +-----------------------------------------------+---------------+ + * + * X 1=B is const, 0=B is reg + * Y 1=C is const, 0=C is reg + * + * In effect OP, OP + 1, OP + 2, and OP + 3 are allocated from the + * 8-bit opcode space for a single logical opcode. The base opcode + * number should be divisible by 4. If the opcode is called 'FOO' + * the following opcode constants would be defined: + * + * DUK_OP_FOO 100 // base opcode number + * DUK_OP_FOO_RR 100 // FOO, B=reg, C=reg + * DUK_OP_FOO_CR 101 // FOO, B=const, C=reg + * DUK_OP_FOO_RC 102 // FOO, B=reg, C=const + * DUK_OP_FOO_CC 103 // FOO, B=const, C=const + * + * If only B or C is a reg/const, the unused opcode combinations can be + * used for other opcodes (which take no reg/const argument). However, + * such opcode values are initially reserved, at least while opcode space + * is available. For example, if 'BAR' uses B for a register field and + * C is a reg/const: + * + * DUK_OP_BAR 116 // base opcode number + * DUK_OP_BAR_RR 116 // BAR, B=reg, C=reg + * DUK_OP_BAR_CR_UNUSED 117 // unused, could be repurposed + * DUK_OP_BAR_RC 118 // BAR, B=reg, C=const + * DUK_OP_BAR_CC_UNUSED 119 // unused, could be repurposed + * + * Macro naming is a bit misleading, e.g. "ABC" in macro name but the + * field layout is concretely "CBA" in the register. */ typedef duk_uint32_t duk_instr_t; -#define DUK_DEC_OP(x) ((x) & 0x3fUL) -#define DUK_DEC_A(x) (((x) >> 6) & 0xffUL) -#define DUK_DEC_B(x) (((x) >> 14) & 0x1ffUL) -#define DUK_DEC_C(x) (((x) >> 23) & 0x1ffUL) -#define DUK_DEC_BC(x) (((x) >> 14) & 0x3ffffUL) -#define DUK_DEC_ABC(x) (((x) >> 6) & 0x3ffffffUL) +#define DUK_BC_SHIFT_OP 0 +#define DUK_BC_SHIFT_A 8 +#define DUK_BC_SHIFT_B 16 +#define DUK_BC_SHIFT_C 24 +#define DUK_BC_SHIFT_BC DUK_BC_SHIFT_B +#define DUK_BC_SHIFT_ABC DUK_BC_SHIFT_A + +#define DUK_BC_UNSHIFTED_MASK_OP 0xffUL +#define DUK_BC_UNSHIFTED_MASK_A 0xffUL +#define DUK_BC_UNSHIFTED_MASK_B 0xffUL +#define DUK_BC_UNSHIFTED_MASK_C 0xffUL +#define DUK_BC_UNSHIFTED_MASK_BC 0xffffUL +#define DUK_BC_UNSHIFTED_MASK_ABC 0xffffffUL + +#define DUK_BC_SHIFTED_MASK_OP (DUK_BC_UNSHIFTED_MASK_OP << DUK_BC_SHIFT_OP) +#define DUK_BC_SHIFTED_MASK_A (DUK_BC_UNSHIFTED_MASK_A << DUK_BC_SHIFT_A) +#define DUK_BC_SHIFTED_MASK_B (DUK_BC_UNSHIFTED_MASK_B << DUK_BC_SHIFT_B) +#define DUK_BC_SHIFTED_MASK_C (DUK_BC_UNSHIFTED_MASK_C << DUK_BC_SHIFT_C) +#define DUK_BC_SHIFTED_MASK_BC (DUK_BC_UNSHIFTED_MASK_BC << DUK_BC_SHIFT_BC) +#define DUK_BC_SHIFTED_MASK_ABC (DUK_BC_UNSHIFTED_MASK_ABC << DUK_BC_SHIFT_ABC) + +#define DUK_DEC_OP(x) ((x) & 0xffUL) +#define DUK_DEC_A(x) (((x) >> 8) & 0xffUL) +#define DUK_DEC_B(x) (((x) >> 16) & 0xffUL) +#define DUK_DEC_C(x) (((x) >> 24) & 0xffUL) +#define DUK_DEC_BC(x) (((x) >> 16) & 0xffffUL) +#define DUK_DEC_ABC(x) (((x) >> 8) & 0xffffffUL) #define DUK_ENC_OP(op) ((duk_instr_t) (op)) #define DUK_ENC_OP_ABC(op,abc) ((duk_instr_t) ( \ - (((duk_instr_t) (abc)) << 6) | \ + (((duk_instr_t) (abc)) << 8) | \ ((duk_instr_t) (op)) \ )) #define DUK_ENC_OP_A_BC(op,a,bc) ((duk_instr_t) ( \ - (((duk_instr_t) (bc)) << 14) | \ - (((duk_instr_t) (a)) << 6) | \ + (((duk_instr_t) (bc)) << 16) | \ + (((duk_instr_t) (a)) << 8) | \ ((duk_instr_t) (op)) \ )) #define DUK_ENC_OP_A_B_C(op,a,b,c) ((duk_instr_t) ( \ - (((duk_instr_t) (c)) << 23) | \ - (((duk_instr_t) (b)) << 14) | \ - (((duk_instr_t) (a)) << 6) | \ + (((duk_instr_t) (c)) << 24) | \ + (((duk_instr_t) (b)) << 16) | \ + (((duk_instr_t) (a)) << 8) | \ ((duk_instr_t) (op)) \ )) -#define DUK_ENC_OP_A_B(op,a,b) DUK_ENC_OP_A_B_C(op,a,b,0) -#define DUK_ENC_OP_A(op,a) DUK_ENC_OP_A_B_C(op,a,0,0) +#define DUK_ENC_OP_A_B(op,a,b) DUK_ENC_OP_A_B_C((op),(a),(b),0) +#define DUK_ENC_OP_A(op,a) DUK_ENC_OP_A_B_C((op),(a),0,0) +#define DUK_ENC_OP_BC(op,bc) DUK_ENC_OP_A_BC((op),0,(bc)) + +/* Get opcode base value with B/C reg/const flags cleared. */ +#define DUK_BC_NOREGCONST_OP(op) ((op) & 0xfc) /* Constants should be signed so that signed arithmetic involving them * won't cause values to be coerced accidentally to unsigned. */ #define DUK_BC_OP_MIN 0 -#define DUK_BC_OP_MAX 0x3fL +#define DUK_BC_OP_MAX 0xffL #define DUK_BC_A_MIN 0 #define DUK_BC_A_MAX 0xffL #define DUK_BC_B_MIN 0 -#define DUK_BC_B_MAX 0x1ffL +#define DUK_BC_B_MAX 0xffL #define DUK_BC_C_MIN 0 -#define DUK_BC_C_MAX 0x1ffL +#define DUK_BC_C_MAX 0xffL #define DUK_BC_BC_MIN 0 -#define DUK_BC_BC_MAX 0x3ffffL +#define DUK_BC_BC_MAX 0xffffL #define DUK_BC_ABC_MIN 0 -#define DUK_BC_ABC_MAX 0x3ffffffL -#define DUK_BC_EXTRAOP_MIN DUK_BC_A_MIN -#define DUK_BC_EXTRAOP_MAX DUK_BC_A_MAX +#define DUK_BC_ABC_MAX 0xffffffL +/* Masks for B/C reg/const indicator in opcode field. */ +#define DUK_BC_REGCONST_B (0x01UL) +#define DUK_BC_REGCONST_C (0x02UL) + +/* Misc. masks for opcode field. */ +#define DUK_BC_INCDECP_FLAG_DEC (0x04UL) +#define DUK_BC_INCDECP_FLAG_POST (0x08UL) + +/* Opcodes. */ #define DUK_OP_LDREG 0 #define DUK_OP_STREG 1 -#define DUK_OP_LDCONST 2 -#define DUK_OP_LDINT 3 -#define DUK_OP_LDINTX 4 -#define DUK_OP_MPUTOBJ 5 -#define DUK_OP_MPUTOBJI 6 -#define DUK_OP_MPUTARR 7 -#define DUK_OP_MPUTARRI 8 -#define DUK_OP_NEW 9 -#define DUK_OP_NEWI 10 -#define DUK_OP_REGEXP 11 -#define DUK_OP_CSREG 12 -#define DUK_OP_CSREGI 13 -#define DUK_OP_GETVAR 14 -#define DUK_OP_PUTVAR 15 -#define DUK_OP_DECLVAR 16 -#define DUK_OP_DELVAR 17 -#define DUK_OP_CSVAR 18 -#define DUK_OP_CSVARI 19 -#define DUK_OP_CLOSURE 20 -#define DUK_OP_GETPROP 21 -#define DUK_OP_PUTPROP 22 -#define DUK_OP_DELPROP 23 -#define DUK_OP_CSPROP 24 -#define DUK_OP_CSPROPI 25 -#define DUK_OP_ADD 26 -#define DUK_OP_SUB 27 -#define DUK_OP_MUL 28 -#define DUK_OP_DIV 29 -#define DUK_OP_MOD 30 -#define DUK_OP_BAND 31 -#define DUK_OP_BOR 32 -#define DUK_OP_BXOR 33 -#define DUK_OP_BASL 34 -#define DUK_OP_BLSR 35 -#define DUK_OP_BASR 36 -#define DUK_OP_EQ 37 -#define DUK_OP_NEQ 38 -#define DUK_OP_SEQ 39 -#define DUK_OP_SNEQ 40 -#define DUK_OP_GT 41 -#define DUK_OP_GE 42 -#define DUK_OP_LT 43 +#define DUK_OP_JUMP 2 +#define DUK_OP_LDCONST 3 +#define DUK_OP_LDINT 4 +#define DUK_OP_LDINTX 5 +#define DUK_OP_LDTHIS 6 +#define DUK_OP_LDUNDEF 7 +#define DUK_OP_LDNULL 8 +#define DUK_OP_LDTRUE 9 +#define DUK_OP_LDFALSE 10 +#define DUK_OP_GETVAR 11 +#define DUK_OP_BNOT 12 +#define DUK_OP_LNOT 13 +#define DUK_OP_UNM 14 +#define DUK_OP_UNP 15 +#define DUK_OP_EQ 16 +#define DUK_OP_EQ_RR 16 +#define DUK_OP_EQ_CR 17 +#define DUK_OP_EQ_RC 18 +#define DUK_OP_EQ_CC 19 +#define DUK_OP_NEQ 20 +#define DUK_OP_NEQ_RR 20 +#define DUK_OP_NEQ_CR 21 +#define DUK_OP_NEQ_RC 22 +#define DUK_OP_NEQ_CC 23 +#define DUK_OP_SEQ 24 +#define DUK_OP_SEQ_RR 24 +#define DUK_OP_SEQ_CR 25 +#define DUK_OP_SEQ_RC 26 +#define DUK_OP_SEQ_CC 27 +#define DUK_OP_SNEQ 28 +#define DUK_OP_SNEQ_RR 28 +#define DUK_OP_SNEQ_CR 29 +#define DUK_OP_SNEQ_RC 30 +#define DUK_OP_SNEQ_CC 31 +#define DUK_OP_GT 32 +#define DUK_OP_GT_RR 32 +#define DUK_OP_GT_CR 33 +#define DUK_OP_GT_RC 34 +#define DUK_OP_GT_CC 35 +#define DUK_OP_GE 36 +#define DUK_OP_GE_RR 36 +#define DUK_OP_GE_CR 37 +#define DUK_OP_GE_RC 38 +#define DUK_OP_GE_CC 39 +#define DUK_OP_LT 40 +#define DUK_OP_LT_RR 40 +#define DUK_OP_LT_CR 41 +#define DUK_OP_LT_RC 42 +#define DUK_OP_LT_CC 43 #define DUK_OP_LE 44 -#define DUK_OP_IF 45 -#define DUK_OP_JUMP 46 -#define DUK_OP_RETURN 47 -#define DUK_OP_CALL 48 -#define DUK_OP_CALLI 49 -#define DUK_OP_TRYCATCH 50 -#define DUK_OP_EXTRA 51 -#define DUK_OP_PREINCR 52 /* pre/post opcode values have constraints, */ -#define DUK_OP_PREDECR 53 /* see duk_js_executor.c */ -#define DUK_OP_POSTINCR 54 -#define DUK_OP_POSTDECR 55 -#define DUK_OP_PREINCV 56 -#define DUK_OP_PREDECV 57 -#define DUK_OP_POSTINCV 58 -#define DUK_OP_POSTDECV 59 -#define DUK_OP_PREINCP 60 -#define DUK_OP_PREDECP 61 -#define DUK_OP_POSTINCP 62 -#define DUK_OP_POSTDECP 63 -#define DUK_OP_NONE 64 /* dummy value used as marker */ +#define DUK_OP_LE_RR 44 +#define DUK_OP_LE_CR 45 +#define DUK_OP_LE_RC 46 +#define DUK_OP_LE_CC 47 +#define DUK_OP_IFTRUE 48 +#define DUK_OP_IFTRUE_R 48 +#define DUK_OP_IFTRUE_C 49 +#define DUK_OP_IFFALSE 50 +#define DUK_OP_IFFALSE_R 50 +#define DUK_OP_IFFALSE_C 51 +#define DUK_OP_ADD 52 +#define DUK_OP_ADD_RR 52 +#define DUK_OP_ADD_CR 53 +#define DUK_OP_ADD_RC 54 +#define DUK_OP_ADD_CC 55 +#define DUK_OP_SUB 56 +#define DUK_OP_SUB_RR 56 +#define DUK_OP_SUB_CR 57 +#define DUK_OP_SUB_RC 58 +#define DUK_OP_SUB_CC 59 +#define DUK_OP_MUL 60 +#define DUK_OP_MUL_RR 60 +#define DUK_OP_MUL_CR 61 +#define DUK_OP_MUL_RC 62 +#define DUK_OP_MUL_CC 63 +#define DUK_OP_DIV 64 +#define DUK_OP_DIV_RR 64 +#define DUK_OP_DIV_CR 65 +#define DUK_OP_DIV_RC 66 +#define DUK_OP_DIV_CC 67 +#define DUK_OP_MOD 68 +#define DUK_OP_MOD_RR 68 +#define DUK_OP_MOD_CR 69 +#define DUK_OP_MOD_RC 70 +#define DUK_OP_MOD_CC 71 +#define DUK_OP_EXP 72 +#define DUK_OP_EXP_RR 72 +#define DUK_OP_EXP_CR 73 +#define DUK_OP_EXP_RC 74 +#define DUK_OP_EXP_CC 75 +#define DUK_OP_BAND 76 +#define DUK_OP_BAND_RR 76 +#define DUK_OP_BAND_CR 77 +#define DUK_OP_BAND_RC 78 +#define DUK_OP_BAND_CC 79 +#define DUK_OP_BOR 80 +#define DUK_OP_BOR_RR 80 +#define DUK_OP_BOR_CR 81 +#define DUK_OP_BOR_RC 82 +#define DUK_OP_BOR_CC 83 +#define DUK_OP_BXOR 84 +#define DUK_OP_BXOR_RR 84 +#define DUK_OP_BXOR_CR 85 +#define DUK_OP_BXOR_RC 86 +#define DUK_OP_BXOR_CC 87 +#define DUK_OP_BASL 88 +#define DUK_OP_BASL_RR 88 +#define DUK_OP_BASL_CR 89 +#define DUK_OP_BASL_RC 90 +#define DUK_OP_BASL_CC 91 +#define DUK_OP_BLSR 92 +#define DUK_OP_BLSR_RR 92 +#define DUK_OP_BLSR_CR 93 +#define DUK_OP_BLSR_RC 94 +#define DUK_OP_BLSR_CC 95 +#define DUK_OP_BASR 96 +#define DUK_OP_BASR_RR 96 +#define DUK_OP_BASR_CR 97 +#define DUK_OP_BASR_RC 98 +#define DUK_OP_BASR_CC 99 +#define DUK_OP_INSTOF 100 +#define DUK_OP_INSTOF_RR 100 +#define DUK_OP_INSTOF_CR 101 +#define DUK_OP_INSTOF_RC 102 +#define DUK_OP_INSTOF_CC 103 +#define DUK_OP_IN 104 +#define DUK_OP_IN_RR 104 +#define DUK_OP_IN_CR 105 +#define DUK_OP_IN_RC 106 +#define DUK_OP_IN_CC 107 +#define DUK_OP_GETPROP 108 +#define DUK_OP_GETPROP_RR 108 +#define DUK_OP_GETPROP_CR 109 +#define DUK_OP_GETPROP_RC 110 +#define DUK_OP_GETPROP_CC 111 +#define DUK_OP_PUTPROP 112 +#define DUK_OP_PUTPROP_RR 112 +#define DUK_OP_PUTPROP_CR 113 +#define DUK_OP_PUTPROP_RC 114 +#define DUK_OP_PUTPROP_CC 115 +#define DUK_OP_DELPROP 116 +#define DUK_OP_DELPROP_RR 116 +#define DUK_OP_DELPROP_CR_UNUSED 117 /* unused now */ +#define DUK_OP_DELPROP_RC 118 +#define DUK_OP_DELPROP_CC_UNUSED 119 /* unused now */ +#define DUK_OP_PREINCR 120 /* pre/post opcode values have constraints, */ +#define DUK_OP_PREDECR 121 /* see duk_js_executor.c and duk_js_compiler.c. */ +#define DUK_OP_POSTINCR 122 +#define DUK_OP_POSTDECR 123 +#define DUK_OP_PREINCV 124 +#define DUK_OP_PREDECV 125 +#define DUK_OP_POSTINCV 126 +#define DUK_OP_POSTDECV 127 +#define DUK_OP_PREINCP 128 /* pre/post inc/dec prop opcodes have constraints */ +#define DUK_OP_PREINCP_RR 128 +#define DUK_OP_PREINCP_CR 129 +#define DUK_OP_PREINCP_RC 130 +#define DUK_OP_PREINCP_CC 131 +#define DUK_OP_PREDECP 132 +#define DUK_OP_PREDECP_RR 132 +#define DUK_OP_PREDECP_CR 133 +#define DUK_OP_PREDECP_RC 134 +#define DUK_OP_PREDECP_CC 135 +#define DUK_OP_POSTINCP 136 +#define DUK_OP_POSTINCP_RR 136 +#define DUK_OP_POSTINCP_CR 137 +#define DUK_OP_POSTINCP_RC 138 +#define DUK_OP_POSTINCP_CC 139 +#define DUK_OP_POSTDECP 140 +#define DUK_OP_POSTDECP_RR 140 +#define DUK_OP_POSTDECP_CR 141 +#define DUK_OP_POSTDECP_RC 142 +#define DUK_OP_POSTDECP_CC 143 +#define DUK_OP_DECLVAR 144 +#define DUK_OP_DECLVAR_RR 144 +#define DUK_OP_DECLVAR_CR 145 +#define DUK_OP_DECLVAR_RC 146 +#define DUK_OP_DECLVAR_CC 147 +#define DUK_OP_REGEXP 148 +#define DUK_OP_REGEXP_RR 148 +#define DUK_OP_REGEXP_CR 149 +#define DUK_OP_REGEXP_RC 150 +#define DUK_OP_REGEXP_CC 151 +#define DUK_OP_CLOSURE 152 +#define DUK_OP_TYPEOF 153 +#define DUK_OP_TYPEOFID 154 +#define DUK_OP_PUTVAR 155 +#define DUK_OP_DELVAR 156 +#define DUK_OP_RETREG 157 +#define DUK_OP_RETUNDEF 158 +#define DUK_OP_RETCONST 159 +#define DUK_OP_RETCONSTN 160 /* return const without incref (e.g. number) */ +#define DUK_OP_LABEL 161 +#define DUK_OP_ENDLABEL 162 +#define DUK_OP_BREAK 163 +#define DUK_OP_CONTINUE 164 +#define DUK_OP_TRYCATCH 165 +#define DUK_OP_ENDTRY 166 +#define DUK_OP_ENDCATCH 167 +#define DUK_OP_ENDFIN 168 +#define DUK_OP_THROW 169 +#define DUK_OP_INVLHS 170 +#define DUK_OP_CSREG 171 +#define DUK_OP_CSVAR 172 +#define DUK_OP_CSVAR_RR 172 +#define DUK_OP_CSVAR_CR 173 +#define DUK_OP_CSVAR_RC 174 +#define DUK_OP_CSVAR_CC 175 +#define DUK_OP_CALL0 176 /* DUK_OP_CALL0 & 0x0F must be zero. */ +#define DUK_OP_CALL1 177 +#define DUK_OP_CALL2 178 +#define DUK_OP_CALL3 179 +#define DUK_OP_CALL4 180 +#define DUK_OP_CALL5 181 +#define DUK_OP_CALL6 182 +#define DUK_OP_CALL7 183 +#define DUK_OP_CALL8 184 +#define DUK_OP_CALL9 185 +#define DUK_OP_CALL10 186 +#define DUK_OP_CALL11 187 +#define DUK_OP_CALL12 188 +#define DUK_OP_CALL13 189 +#define DUK_OP_CALL14 190 +#define DUK_OP_CALL15 191 +#define DUK_OP_NEWOBJ 192 +#define DUK_OP_NEWARR 193 +#define DUK_OP_MPUTOBJ 194 +#define DUK_OP_MPUTOBJI 195 +#define DUK_OP_INITSET 196 +#define DUK_OP_INITGET 197 +#define DUK_OP_MPUTARR 198 +#define DUK_OP_MPUTARRI 199 +#define DUK_OP_SETALEN 200 +#define DUK_OP_INITENUM 201 +#define DUK_OP_NEXTENUM 202 +#define DUK_OP_NEWTARGET 203 +#define DUK_OP_DEBUGGER 204 +#define DUK_OP_NOP 205 +#define DUK_OP_INVALID 206 +#define DUK_OP_UNUSED207 207 +#define DUK_OP_GETPROPC 208 +#define DUK_OP_GETPROPC_RR 208 +#define DUK_OP_GETPROPC_CR 209 +#define DUK_OP_GETPROPC_RC 210 +#define DUK_OP_GETPROPC_CC 211 +#define DUK_OP_UNUSED212 212 +#define DUK_OP_UNUSED213 213 +#define DUK_OP_UNUSED214 214 +#define DUK_OP_UNUSED215 215 +#define DUK_OP_UNUSED216 216 +#define DUK_OP_UNUSED217 217 +#define DUK_OP_UNUSED218 218 +#define DUK_OP_UNUSED219 219 +#define DUK_OP_UNUSED220 220 +#define DUK_OP_UNUSED221 221 +#define DUK_OP_UNUSED222 222 +#define DUK_OP_UNUSED223 223 +#define DUK_OP_UNUSED224 224 +#define DUK_OP_UNUSED225 225 +#define DUK_OP_UNUSED226 226 +#define DUK_OP_UNUSED227 227 +#define DUK_OP_UNUSED228 228 +#define DUK_OP_UNUSED229 229 +#define DUK_OP_UNUSED230 230 +#define DUK_OP_UNUSED231 231 +#define DUK_OP_UNUSED232 232 +#define DUK_OP_UNUSED233 233 +#define DUK_OP_UNUSED234 234 +#define DUK_OP_UNUSED235 235 +#define DUK_OP_UNUSED236 236 +#define DUK_OP_UNUSED237 237 +#define DUK_OP_UNUSED238 238 +#define DUK_OP_UNUSED239 239 +#define DUK_OP_UNUSED240 240 +#define DUK_OP_UNUSED241 241 +#define DUK_OP_UNUSED242 242 +#define DUK_OP_UNUSED243 243 +#define DUK_OP_UNUSED244 244 +#define DUK_OP_UNUSED245 245 +#define DUK_OP_UNUSED246 246 +#define DUK_OP_UNUSED247 247 +#define DUK_OP_UNUSED248 248 +#define DUK_OP_UNUSED249 249 +#define DUK_OP_UNUSED250 250 +#define DUK_OP_UNUSED251 251 +#define DUK_OP_UNUSED252 252 +#define DUK_OP_UNUSED253 253 +#define DUK_OP_UNUSED254 254 +#define DUK_OP_UNUSED255 255 +#define DUK_OP_NONE 256 /* dummy value used as marker (doesn't fit in 8-bit field) */ -/* DUK_OP_EXTRA, sub-operation in A */ -#define DUK_EXTRAOP_NOP 0 -#define DUK_EXTRAOP_INVALID 1 -#define DUK_EXTRAOP_LDTHIS 2 -#define DUK_EXTRAOP_LDUNDEF 3 -#define DUK_EXTRAOP_LDNULL 4 -#define DUK_EXTRAOP_LDTRUE 5 -#define DUK_EXTRAOP_LDFALSE 6 -#define DUK_EXTRAOP_NEWOBJ 7 -#define DUK_EXTRAOP_NEWARR 8 -#define DUK_EXTRAOP_SETALEN 9 -#define DUK_EXTRAOP_TYPEOF 10 -#define DUK_EXTRAOP_TYPEOFID 11 -#define DUK_EXTRAOP_INITENUM 12 -#define DUK_EXTRAOP_NEXTENUM 13 -#define DUK_EXTRAOP_INITSET 14 -#define DUK_EXTRAOP_INITSETI 15 -#define DUK_EXTRAOP_INITGET 16 -#define DUK_EXTRAOP_INITGETI 17 -#define DUK_EXTRAOP_ENDTRY 18 -#define DUK_EXTRAOP_ENDCATCH 19 -#define DUK_EXTRAOP_ENDFIN 20 -#define DUK_EXTRAOP_THROW 21 -#define DUK_EXTRAOP_INVLHS 22 -#define DUK_EXTRAOP_UNM 23 -#define DUK_EXTRAOP_UNP 24 -#define DUK_EXTRAOP_DEBUGGER 25 -#define DUK_EXTRAOP_BREAK 26 -#define DUK_EXTRAOP_CONTINUE 27 -#define DUK_EXTRAOP_BNOT 28 -#define DUK_EXTRAOP_LNOT 29 -#define DUK_EXTRAOP_INSTOF 30 -#define DUK_EXTRAOP_IN 31 -#define DUK_EXTRAOP_LABEL 32 -#define DUK_EXTRAOP_ENDLABEL 33 +/* XXX: Allocate flags from opcode field? Would take 16 opcode slots + * but avoids shuffling in more cases. Maybe not worth it. + */ +/* DUK_OP_TRYCATCH flags in A. */ +#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH (1U << 0) +#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY (1U << 1) +#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING (1U << 2) +#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING (1U << 3) -/* DUK_OP_CALL flags in A */ -#define DUK_BC_CALL_FLAG_TAILCALL (1 << 0) -#define DUK_BC_CALL_FLAG_EVALCALL (1 << 1) +/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags + * (DUK_PROPDESC_FLAG_XXX). + */ +#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1U << 4) /* function declaration */ -/* DUK_OP_TRYCATCH flags in A */ -#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH (1 << 0) -#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY (1 << 1) -#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING (1 << 2) -#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING (1 << 3) +/* DUK_OP_CALLn flags, part of opcode field. Three lowest bits must match + * DUK_CALL_FLAG_xxx directly. + */ +#define DUK_BC_CALL_FLAG_TAILCALL (1U << 0) +#define DUK_BC_CALL_FLAG_CONSTRUCT (1U << 1) +#define DUK_BC_CALL_FLAG_CALLED_AS_EVAL (1U << 2) +#define DUK_BC_CALL_FLAG_INDIRECT (1U << 3) -/* DUK_OP_RETURN flags in A */ -#define DUK_BC_RETURN_FLAG_HAVE_RETVAL (1 << 0) - -/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags (DUK_PROPDESC_FLAG_XXX) */ -#define DUK_BC_DECLVAR_FLAG_UNDEF_VALUE (1 << 4) /* use 'undefined' for value automatically */ -#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1 << 5) /* function declaration */ - -/* misc constants and helper macros */ -#define DUK_BC_REGLIMIT 256 /* if B/C is >= this value, refers to a const */ -#define DUK_BC_ISREG(x) ((x) < DUK_BC_REGLIMIT) -#define DUK_BC_ISCONST(x) ((x) >= DUK_BC_REGLIMIT) -#define DUK_BC_LDINT_BIAS (1L << 17) -#define DUK_BC_LDINTX_SHIFT 18 -#define DUK_BC_JUMP_BIAS (1L << 25) +/* Misc constants and helper macros. */ +#define DUK_BC_LDINT_BIAS (1L << 15) +#define DUK_BC_LDINTX_SHIFT 16 +#define DUK_BC_JUMP_BIAS (1L << 23) #endif /* DUK_JS_BYTECODE_H_INCLUDED */ -#line 1 "duk_lexer.h" +/* #include duk_lexer.h */ /* * Lexer defines. */ -#ifndef DUK_LEXER_H_INCLUDED +#if !defined(DUK_LEXER_H_INCLUDED) #define DUK_LEXER_H_INCLUDED typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct); @@ -2752,10 +3734,9 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo #define DUK_LEXER_SETPOINT(ctx,pt) duk_lexer_setpoint((ctx), (pt)) -#define DUK_LEXER_GETPOINT(ctx,pt) do { (pt)->offset = (ctx)->window[0].offset; \ - (pt)->line = (ctx)->window[0].line; } while (0) +#define DUK_LEXER_GETPOINT(ctx,pt) duk_lexer_getpoint((ctx), (pt)) -/* currently 6 characters of lookup are actually needed (duk_lexer.c) */ +/* Currently 6 characters of lookup are actually needed (duk_lexer.c). */ #define DUK_LEXER_WINDOW_SIZE 6 #if defined(DUK_USE_LEXER_SLIDING_WINDOW) #define DUK_LEXER_BUFFER_SIZE 64 @@ -2857,41 +3838,45 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo #define DUK_TOK_MUL 68 #define DUK_TOK_DIV 69 #define DUK_TOK_MOD 70 -#define DUK_TOK_INCREMENT 71 -#define DUK_TOK_DECREMENT 72 -#define DUK_TOK_ALSHIFT 73 /* named "arithmetic" because result is signed */ -#define DUK_TOK_ARSHIFT 74 -#define DUK_TOK_RSHIFT 75 -#define DUK_TOK_BAND 76 -#define DUK_TOK_BOR 77 -#define DUK_TOK_BXOR 78 -#define DUK_TOK_LNOT 79 -#define DUK_TOK_BNOT 80 -#define DUK_TOK_LAND 81 -#define DUK_TOK_LOR 82 -#define DUK_TOK_QUESTION 83 -#define DUK_TOK_COLON 84 -#define DUK_TOK_EQUALSIGN 85 -#define DUK_TOK_ADD_EQ 86 -#define DUK_TOK_SUB_EQ 87 -#define DUK_TOK_MUL_EQ 88 -#define DUK_TOK_DIV_EQ 89 -#define DUK_TOK_MOD_EQ 90 -#define DUK_TOK_ALSHIFT_EQ 91 -#define DUK_TOK_ARSHIFT_EQ 92 -#define DUK_TOK_RSHIFT_EQ 93 -#define DUK_TOK_BAND_EQ 94 -#define DUK_TOK_BOR_EQ 95 -#define DUK_TOK_BXOR_EQ 96 +#define DUK_TOK_EXP 71 +#define DUK_TOK_INCREMENT 72 +#define DUK_TOK_DECREMENT 73 +#define DUK_TOK_ALSHIFT 74 /* named "arithmetic" because result is signed */ +#define DUK_TOK_ARSHIFT 75 +#define DUK_TOK_RSHIFT 76 +#define DUK_TOK_BAND 77 +#define DUK_TOK_BOR 78 +#define DUK_TOK_BXOR 79 +#define DUK_TOK_LNOT 80 +#define DUK_TOK_BNOT 81 +#define DUK_TOK_LAND 82 +#define DUK_TOK_LOR 83 +#define DUK_TOK_QUESTION 84 +#define DUK_TOK_COLON 85 +#define DUK_TOK_EQUALSIGN 86 +#define DUK_TOK_ADD_EQ 87 +#define DUK_TOK_SUB_EQ 88 +#define DUK_TOK_MUL_EQ 89 +#define DUK_TOK_DIV_EQ 90 +#define DUK_TOK_MOD_EQ 91 +#define DUK_TOK_EXP_EQ 92 +#define DUK_TOK_ALSHIFT_EQ 93 +#define DUK_TOK_ARSHIFT_EQ 94 +#define DUK_TOK_RSHIFT_EQ 95 +#define DUK_TOK_BAND_EQ 96 +#define DUK_TOK_BOR_EQ 97 +#define DUK_TOK_BXOR_EQ 98 /* literals (E5 Section 7.8), except null, true, false, which are treated * like reserved words (above). */ -#define DUK_TOK_NUMBER 97 -#define DUK_TOK_STRING 98 -#define DUK_TOK_REGEXP 99 +#define DUK_TOK_NUMBER 99 +#define DUK_TOK_STRING 100 +#define DUK_TOK_REGEXP 101 -#define DUK_TOK_MAXVAL 99 /* inclusive */ +#define DUK_TOK_MAXVAL 101 /* inclusive */ + +#define DUK_TOK_INVALID DUK_SMALL_UINT_MAX /* Convert heap string index to a token (reserved words) */ #define DUK_STRIDX_TO_TOK(x) ((x) - DUK_STRIDX_START_RESERVED + DUK_TOK_START_RESERVED) @@ -3050,12 +4035,12 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo #define DUK_RETOK_ASSERT_START_NEG_LOOKAHEAD 8 #define DUK_RETOK_ATOM_PERIOD 9 #define DUK_RETOK_ATOM_CHAR 10 -#define DUK_RETOK_ATOM_DIGIT 11 -#define DUK_RETOK_ATOM_NOT_DIGIT 12 -#define DUK_RETOK_ATOM_WHITE 13 -#define DUK_RETOK_ATOM_NOT_WHITE 14 -#define DUK_RETOK_ATOM_WORD_CHAR 15 -#define DUK_RETOK_ATOM_NOT_WORD_CHAR 16 +#define DUK_RETOK_ATOM_DIGIT 11 /* assumptions in regexp compiler */ +#define DUK_RETOK_ATOM_NOT_DIGIT 12 /* -""- */ +#define DUK_RETOK_ATOM_WHITE 13 /* -""- */ +#define DUK_RETOK_ATOM_NOT_WHITE 14 /* -""- */ +#define DUK_RETOK_ATOM_WORD_CHAR 15 /* -""- */ +#define DUK_RETOK_ATOM_NOT_WORD_CHAR 16 /* -""- */ #define DUK_RETOK_ATOM_BACKREFERENCE 17 #define DUK_RETOK_ATOM_START_CAPTURE_GROUP 18 #define DUK_RETOK_ATOM_START_NONCAPTURE_GROUP 19 @@ -3071,8 +4056,8 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo * stale values otherwise. */ struct duk_token { - duk_small_int_t t; /* token type (with reserved word identification) */ - duk_small_int_t t_nores; /* token type (with reserved words as DUK_TOK_IDENTIFER) */ + duk_small_uint_t t; /* token type (with reserved word identification) */ + duk_small_uint_t t_nores; /* token type (with reserved words as DUK_TOK_IDENTIFER) */ duk_double_t num; /* numeric value of token */ duk_hstring *str1; /* string 1 of token (borrowed, stored to ctx->slot1_idx) */ duk_hstring *str2; /* string 2 of token (borrowed, stored to ctx->slot2_idx) */ @@ -3087,11 +4072,11 @@ struct duk_token { /* A regexp token value. */ struct duk_re_token { - duk_small_int_t t; /* token type */ - duk_small_int_t greedy; - duk_uint_fast32_t num; /* numeric value (character, count) */ - duk_uint_fast32_t qmin; - duk_uint_fast32_t qmax; + duk_small_uint_t t; /* token type */ + duk_small_uint_t greedy; + duk_uint32_t num; /* numeric value (character, count) */ + duk_uint32_t qmin; + duk_uint32_t qmax; }; /* A structure for 'snapshotting' a point for rewinding */ @@ -3107,7 +4092,7 @@ struct duk_lexer_codepoint { duk_int_t line; }; -/* Lexer context. Same context is used for Ecmascript and Regexp parsing. */ +/* Lexer context. Same context is used for ECMAScript and Regexp parsing. */ struct duk_lexer_ctx { #if defined(DUK_USE_LEXER_SLIDING_WINDOW) duk_lexer_codepoint *window; /* unicode code points, window[0] is always next, points to 'buffer' */ @@ -3131,6 +4116,8 @@ struct duk_lexer_ctx { duk_int_t token_count; /* number of tokens parsed */ duk_int_t token_limit; /* maximum token count before error (sanity backstop) */ + + duk_small_uint_t flags; /* lexer flags, use compiler flag defines for now */ }; /* @@ -3139,6 +4126,7 @@ struct duk_lexer_ctx { DUK_INTERNAL_DECL void duk_lexer_initctx(duk_lexer_ctx *lex_ctx); +DUK_INTERNAL_DECL void duk_lexer_getpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt); DUK_INTERNAL_DECL void duk_lexer_setpoint(duk_lexer_ctx *lex_ctx, duk_lexer_point *pt); DUK_INTERNAL_DECL @@ -3146,21 +4134,21 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, duk_token *out_token, duk_bool_t strict_mode, duk_bool_t regexp_mode); -#ifdef DUK_USE_REGEXP_SUPPORT +#if defined(DUK_USE_REGEXP_SUPPORT) DUK_INTERNAL_DECL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token); DUK_INTERNAL_DECL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range_callback gen_range, void *userdata); #endif /* DUK_USE_REGEXP_SUPPORT */ #endif /* DUK_LEXER_H_INCLUDED */ -#line 1 "duk_js_compiler.h" +/* #include duk_js_compiler.h */ /* - * Ecmascript compiler. + * ECMAScript compiler. */ -#ifndef DUK_JS_COMPILER_H_INCLUDED +#if !defined(DUK_JS_COMPILER_H_INCLUDED) #define DUK_JS_COMPILER_H_INCLUDED -/* ecmascript compiler limits */ +/* ECMAScript compiler limits */ #define DUK_COMPILER_TOKEN_LIMIT 100000000L /* 1e8: protects against deeply nested inner functions */ /* maximum loopcount for peephole optimization */ @@ -3180,22 +4168,23 @@ DUK_INTERNAL_DECL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_ #define DUK_IVAL_NONE 0 /* no value */ #define DUK_IVAL_PLAIN 1 /* register, constant, or value */ #define DUK_IVAL_ARITH 2 /* binary arithmetic; DUK_OP_ADD, DUK_OP_EQ, other binary ops */ -#define DUK_IVAL_ARITH_EXTRAOP 3 /* binary arithmetic using extraops; DUK_EXTRAOP_INSTOF etc */ -#define DUK_IVAL_PROP 4 /* property access */ -#define DUK_IVAL_VAR 5 /* variable access */ +#define DUK_IVAL_PROP 3 /* property access */ +#define DUK_IVAL_VAR 4 /* variable access */ #define DUK_ISPEC_NONE 0 /* no value */ #define DUK_ISPEC_VALUE 1 /* value resides in 'valstack_idx' */ #define DUK_ISPEC_REGCONST 2 /* value resides in a register or constant */ -/* bit mask which indicates that a regconst is a constant instead of a register */ -#define DUK_JS_CONST_MARKER 0x80000000UL +/* Bit mask which indicates that a regconst is a constant instead of a register. + * Chosen so that when a regconst is cast to duk_int32_t, all consts are + * negative values. + */ +#define DUK_REGCONST_CONST_MARKER DUK_INT32_MIN /* = -0x80000000 */ -/* type to represent a reg/const reference during compilation */ -typedef duk_uint32_t duk_regconst_t; - -/* type to represent a straight register reference, with <0 indicating none */ -typedef duk_int32_t duk_reg_t; +/* Type to represent a reg/const reference during compilation, with <0 + * indicating a constant. Some call sites also use -1 to indicate 'none'. + */ +typedef duk_int32_t duk_regconst_t; typedef struct { duk_small_uint_t t; /* DUK_ISPEC_XXX */ @@ -3213,7 +4202,7 @@ typedef struct { /* XXX: can be optimized for smaller footprint esp. on 32-bit environments */ duk_small_uint_t t; /* DUK_IVAL_XXX */ - duk_small_uint_t op; /* bytecode opcode (or extraop) for binary ops */ + duk_small_uint_t op; /* bytecode opcode for binary ops */ duk_ispec x1; duk_ispec x2; } duk_ivalue; @@ -3235,8 +4224,8 @@ struct duk_compiler_instr { * Compiler state */ -#define DUK_LABEL_FLAG_ALLOW_BREAK (1 << 0) -#define DUK_LABEL_FLAG_ALLOW_CONTINUE (1 << 1) +#define DUK_LABEL_FLAG_ALLOW_BREAK (1U << 0) +#define DUK_LABEL_FLAG_ALLOW_CONTINUE (1U << 1) #define DUK_DECL_TYPE_VAR 0 #define DUK_DECL_TYPE_FUNC 1 @@ -3259,7 +4248,7 @@ typedef struct { */ } duk_labelinfo; -/* Compiling state of one function, eventually converted to duk_hcompiledfunction */ +/* Compiling state of one function, eventually converted to duk_hcompfunc */ struct duk_compiler_func { /* These pointers are at the start of the struct so that they pack * nicely. Mixing pointers and integer values is bad on some @@ -3283,7 +4272,7 @@ struct duk_compiler_func { duk_hobject *h_argnames; /* array of formal argument names (-> _Formals) */ duk_hobject *h_varmap; /* variable map for pass 2 (identifier -> register number or null (unmapped)) */ - /* value stack indices for tracking objects */ + /* Value stack indices for tracking objects. */ /* code_idx: not needed */ duk_idx_t consts_idx; duk_idx_t funcs_idx; @@ -3293,52 +4282,55 @@ struct duk_compiler_func { duk_idx_t argnames_idx; duk_idx_t varmap_idx; - /* temp reg handling */ - duk_reg_t temp_first; /* first register that is a temporary (below: variables) */ - duk_reg_t temp_next; /* next temporary register to allocate */ - duk_reg_t temp_max; /* highest value of temp_reg (temp_max - 1 is highest used reg) */ + /* Temp reg handling. */ + duk_regconst_t temp_first; /* first register that is a temporary (below: variables) */ + duk_regconst_t temp_next; /* next temporary register to allocate */ + duk_regconst_t temp_max; /* highest value of temp_reg (temp_max - 1 is highest used reg) */ - /* shuffle registers if large number of regs/consts */ - duk_reg_t shuffle1; - duk_reg_t shuffle2; - duk_reg_t shuffle3; + /* Shuffle registers if large number of regs/consts. */ + duk_regconst_t shuffle1; + duk_regconst_t shuffle2; + duk_regconst_t shuffle3; - /* stats for current expression being parsed */ + /* Stats for current expression being parsed. */ duk_int_t nud_count; duk_int_t led_count; duk_int_t paren_level; /* parenthesis count, 0 = top level */ duk_bool_t expr_lhs; /* expression is left-hand-side compatible */ duk_bool_t allow_in; /* current paren level allows 'in' token */ - /* misc */ + /* Misc. */ duk_int_t stmt_next; /* statement id allocation (running counter) */ duk_int_t label_next; /* label id allocation (running counter) */ duk_int_t catch_depth; /* catch stack depth */ duk_int_t with_depth; /* with stack depth (affects identifier lookups) */ duk_int_t fnum_next; /* inner function numbering */ duk_int_t num_formals; /* number of formal arguments */ - duk_reg_t reg_stmt_value; /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */ + duk_regconst_t reg_stmt_value; /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_int_t min_line; /* XXX: typing (duk_hcompiledfunction has duk_uint32_t) */ + duk_int_t min_line; /* XXX: typing (duk_hcompfunc has duk_uint32_t) */ duk_int_t max_line; #endif - /* status booleans */ - duk_bool_t is_function; /* is an actual function (not global/eval code) */ - duk_bool_t is_eval; /* is eval code */ - duk_bool_t is_global; /* is global code */ - duk_bool_t is_setget; /* is a setter/getter */ - duk_bool_t is_decl; /* is a function declaration (as opposed to function expression) */ - duk_bool_t is_strict; /* function is strict */ - duk_bool_t is_notail; /* function must not be tail called */ - duk_bool_t in_directive_prologue; /* parsing in "directive prologue", recognize directives */ - duk_bool_t in_scanning; /* parsing in "scanning" phase (first pass) */ - duk_bool_t may_direct_eval; /* function may call direct eval */ - duk_bool_t id_access_arguments; /* function refers to 'arguments' identifier */ - duk_bool_t id_access_slow; /* function makes one or more slow path accesses */ - duk_bool_t is_arguments_shadowed; /* argument/function declaration shadows 'arguments' */ - duk_bool_t needs_shuffle; /* function needs shuffle registers */ - duk_bool_t reject_regexp_in_adv; /* reject RegExp literal on next advance() call; needed for handling IdentifierName productions */ + /* Status booleans. */ + duk_uint8_t is_function; /* is an actual function (not global/eval code) */ + duk_uint8_t is_eval; /* is eval code */ + duk_uint8_t is_global; /* is global code */ + duk_uint8_t is_namebinding; /* needs a name binding */ + duk_uint8_t is_constructable; /* result is constructable */ + duk_uint8_t is_setget; /* is a setter/getter */ + duk_uint8_t is_strict; /* function is strict */ + duk_uint8_t is_notail; /* function must not be tail called */ + duk_uint8_t in_directive_prologue; /* parsing in "directive prologue", recognize directives */ + duk_uint8_t in_scanning; /* parsing in "scanning" phase (first pass) */ + duk_uint8_t may_direct_eval; /* function may call direct eval */ + duk_uint8_t id_access_arguments; /* function refers to 'arguments' identifier */ + duk_uint8_t id_access_slow; /* function makes one or more slow path accesses that won't match own static variables */ + duk_uint8_t id_access_slow_own; /* function makes one or more slow path accesses that may match own static variables */ + duk_uint8_t is_arguments_shadowed; /* argument/function declaration shadows 'arguments' */ + duk_uint8_t needs_shuffle; /* function needs shuffle registers */ + duk_uint8_t reject_regexp_in_adv; /* reject RegExp literal on next advance() call; needed for handling IdentifierName productions */ + duk_uint8_t allow_regexp_in_adv; /* allow RegExp literal on next advance() call */ }; struct duk_compiler_ctx { @@ -3373,19 +4365,15 @@ struct duk_compiler_ctx { * Prototypes */ -#define DUK_JS_COMPILE_FLAG_EVAL (1 << 0) /* source is eval code (not global) */ -#define DUK_JS_COMPILE_FLAG_STRICT (1 << 1) /* strict outer context */ -#define DUK_JS_COMPILE_FLAG_FUNCEXPR (1 << 2) /* source is a function expression (used for Function constructor) */ - DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags); #endif /* DUK_JS_COMPILER_H_INCLUDED */ -#line 1 "duk_regexp.h" +/* #include duk_regexp.h */ /* * Regular expression structs, constants, and bytecode defines. */ -#ifndef DUK_REGEXP_H_INCLUDED +#if !defined(DUK_REGEXP_H_INCLUDED) #define DUK_REGEXP_H_INCLUDED /* maximum bytecode copies for {n,m} quantifiers */ @@ -3419,9 +4407,9 @@ DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_b #define DUK_REOP_ASSERT_NOT_WORD_BOUNDARY 19 /* flags */ -#define DUK_RE_FLAG_GLOBAL (1 << 0) -#define DUK_RE_FLAG_IGNORE_CASE (1 << 1) -#define DUK_RE_FLAG_MULTILINE (1 << 2) +#define DUK_RE_FLAG_GLOBAL (1U << 0) +#define DUK_RE_FLAG_IGNORE_CASE (1U << 1) +#define DUK_RE_FLAG_MULTILINE (1U << 2) struct duk_re_matcher_ctx { duk_hthread *thr; @@ -3457,19 +4445,21 @@ struct duk_re_compiler_ctx { * Prototypes */ +#if defined(DUK_USE_REGEXP_SUPPORT) DUK_INTERNAL_DECL void duk_regexp_compile(duk_hthread *thr); DUK_INTERNAL_DECL void duk_regexp_create_instance(duk_hthread *thr); DUK_INTERNAL_DECL void duk_regexp_match(duk_hthread *thr); DUK_INTERNAL_DECL void duk_regexp_match_force_global(duk_hthread *thr); /* hacky helper for String.prototype.split() */ +#endif #endif /* DUK_REGEXP_H_INCLUDED */ -#line 1 "duk_heaphdr.h" +/* #include duk_heaphdr.h */ /* * Heap header definition and assorted macros, including ref counting. * Access all fields through the accessor macros. */ -#ifndef DUK_HEAPHDR_H_INCLUDED +#if !defined(DUK_HEAPHDR_H_INCLUDED) #define DUK_HEAPHDR_H_INCLUDED /* @@ -3477,30 +4467,45 @@ DUK_INTERNAL_DECL void duk_regexp_match_force_global(duk_hthread *thr); /* hack * * All heap objects share the same flags and refcount fields. Objects other * than strings also need to have a single or double linked list pointers - * for insertion into the "heap allocated" list. Strings are held in the - * heap-wide string table so they don't need link pointers. + * for insertion into the "heap allocated" list. Strings have single linked + * list pointers for string table chaining. * * Technically, 'h_refcount' must be wide enough to guarantee that it cannot - * wrap (otherwise objects might be freed incorrectly after wrapping). This - * means essentially that the refcount field must be as wide as data pointers. - * On 64-bit platforms this means that the refcount needs to be 64 bits even - * if an 'int' is 32 bits. This is a bit unfortunate, and compromising on - * this might be reasonable in the future. + * wrap; otherwise objects might be freed incorrectly after wrapping. The + * default refcount field is 32 bits even on 64-bit systems: while that's in + * theory incorrect, the Duktape heap needs to be larger than 64GB for the + * count to actually wrap (assuming 16-byte duk_tvals). This is very unlikely + * to ever be an issue, but if it is, disabling DUK_USE_REFCOUNT32 causes + * Duktape to use size_t for refcounts which should always be safe. * * Heap header size on 32-bit platforms: 8 bytes without reference counting, * 16 bytes with reference counting. + * + * Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not + * defined without DUK_USE_REFERENCE_COUNTING, so caller must #if defined() + * around them. */ +/* XXX: macro for shared header fields (avoids some padding issues) */ + struct duk_heaphdr { duk_uint32_t h_flags; #if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_ASSERTIONS) + /* When assertions enabled, used by mark-and-sweep for refcount + * validation. Largest reasonable type; also detects overflows. + */ + duk_size_t h_assert_refcount; +#endif #if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount16; + duk_uint16_t h_refcount; +#elif defined(DUK_USE_REFCOUNT32) + duk_uint32_t h_refcount; #else duk_size_t h_refcount; #endif -#endif +#endif /* DUK_USE_REFERENCE_COUNTING */ #if defined(DUK_USE_HEAPPTR16) duk_uint16_t h_next16; @@ -3540,13 +4545,26 @@ struct duk_heaphdr_string { duk_uint32_t h_flags; #if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_ASSERTIONS) + /* When assertions enabled, used by mark-and-sweep for refcount + * validation. Largest reasonable type; also detects overflows. + */ + duk_size_t h_assert_refcount; +#endif #if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount16; + duk_uint16_t h_refcount; duk_uint16_t h_strextra16; /* round out to 8 bytes */ +#elif defined(DUK_USE_REFCOUNT32) + duk_uint32_t h_refcount; #else duk_size_t h_refcount; #endif -#endif +#else + duk_uint16_t h_strextra16; +#endif /* DUK_USE_REFERENCE_COUNTING */ + + duk_hstring *h_next; + /* No 'h_prev' pointer for strings. */ }; #define DUK_HEAPHDR_FLAGS_TYPE_MASK 0x00000003UL @@ -3567,11 +4585,11 @@ struct duk_heaphdr_string { #define DUK_HEAPHDR_FLAG_FINALIZED DUK_HEAPHDR_HEAP_FLAG(3) /* mark-and-sweep: finalized (on previous pass) */ #define DUK_HEAPHDR_FLAG_READONLY DUK_HEAPHDR_HEAP_FLAG(4) /* read-only object, in code section */ -#define DUK_HTYPE_MIN 1 -#define DUK_HTYPE_STRING 1 -#define DUK_HTYPE_OBJECT 2 -#define DUK_HTYPE_BUFFER 3 -#define DUK_HTYPE_MAX 3 +#define DUK_HTYPE_MIN 0 +#define DUK_HTYPE_STRING 0 +#define DUK_HTYPE_OBJECT 1 +#define DUK_HTYPE_BUFFER 2 +#define DUK_HTYPE_MAX 2 #if defined(DUK_USE_HEAPPTR16) #define DUK_HEAPHDR_GET_NEXT(heap,h) \ @@ -3602,23 +4620,15 @@ struct duk_heaphdr_string { #endif #if defined(DUK_USE_REFERENCE_COUNTING) -#if defined(DUK_USE_REFCOUNT16) -#define DUK_HEAPHDR_GET_REFCOUNT(h) ((h)->h_refcount16) -#define DUK_HEAPHDR_SET_REFCOUNT(h,val) do { \ - (h)->h_refcount16 = (val); \ - } while (0) -#define DUK_HEAPHDR_PREINC_REFCOUNT(h) (++(h)->h_refcount16) /* result: updated refcount */ -#define DUK_HEAPHDR_PREDEC_REFCOUNT(h) (--(h)->h_refcount16) /* result: updated refcount */ -#else #define DUK_HEAPHDR_GET_REFCOUNT(h) ((h)->h_refcount) #define DUK_HEAPHDR_SET_REFCOUNT(h,val) do { \ (h)->h_refcount = (val); \ + DUK_ASSERT((h)->h_refcount == (val)); /* No truncation. */ \ } while (0) #define DUK_HEAPHDR_PREINC_REFCOUNT(h) (++(h)->h_refcount) /* result: updated refcount */ #define DUK_HEAPHDR_PREDEC_REFCOUNT(h) (--(h)->h_refcount) /* result: updated refcount */ -#endif #else -/* refcount macros not defined without refcounting, caller must #ifdef now */ +/* refcount macros not defined without refcounting, caller must #if defined() now */ #endif /* DUK_USE_REFERENCE_COUNTING */ /* @@ -3627,19 +4637,22 @@ struct duk_heaphdr_string { */ #define DUK_HEAPHDR_GET_FLAGS_RAW(h) ((h)->h_flags) - +#define DUK_HEAPHDR_SET_FLAGS_RAW(h,val) do { \ + (h)->h_flags = (val); } \ + } #define DUK_HEAPHDR_GET_FLAGS(h) ((h)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK) #define DUK_HEAPHDR_SET_FLAGS(h,val) do { \ (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_FLAG_MASK)) | (val); \ } while (0) - #define DUK_HEAPHDR_GET_TYPE(h) ((h)->h_flags & DUK_HEAPHDR_FLAGS_TYPE_MASK) #define DUK_HEAPHDR_SET_TYPE(h,val) do { \ (h)->h_flags = ((h)->h_flags & ~(DUK_HEAPHDR_FLAGS_TYPE_MASK)) | (val); \ } while (0) +/* Comparison for type >= DUK_HTYPE_MIN skipped; because DUK_HTYPE_MIN is zero + * and the comparison is unsigned, it's always true and generates warnings. + */ #define DUK_HEAPHDR_HTYPE_VALID(h) ( \ - DUK_HEAPHDR_GET_TYPE((h)) >= DUK_HTYPE_MIN && \ DUK_HEAPHDR_GET_TYPE((h)) <= DUK_HTYPE_MAX \ ) @@ -3685,7 +4698,7 @@ struct duk_heaphdr_string { #define DUK_HEAPHDR_SET_FLAG_RANGE(h,m,n,v) do { \ (h)->h_flags = \ - ((h)->h_flags & (~(((1 << (n)) - 1) << (m)))) \ + ((h)->h_flags & (~(((1UL << (n)) - 1UL) << (m)))) \ | ((v) << (m)); \ } while (0) @@ -3701,7 +4714,23 @@ struct duk_heaphdr_string { } while (0) #endif -#define DUK_HEAPHDR_STRING_INIT_NULLS(h) /* currently nop */ +#define DUK_HEAPHDR_STRING_INIT_NULLS(h) do { \ + (h)->h_next = NULL; \ + } while (0) + +/* + * Type tests + */ + +/* Take advantage of the fact that for DUK_HTYPE_xxx numbers the lowest bit + * is only set for DUK_HTYPE_OBJECT (= 1). + */ +#if 0 +#define DUK_HEAPHDR_IS_OBJECT(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_OBJECT) +#endif +#define DUK_HEAPHDR_IS_OBJECT(h) ((h)->h_flags & 0x01UL) +#define DUK_HEAPHDR_IS_STRING(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_STRING) +#define DUK_HEAPHDR_IS_BUFFER(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_BUFFER) /* * Assert helpers @@ -3710,31 +4739,29 @@ struct duk_heaphdr_string { /* Check that prev/next links are consistent: if e.g. h->prev is != NULL, * h->prev->next should point back to h. */ -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_ASSERTIONS) -#define DUK_ASSERT_HEAPHDR_LINKS(heap,h) do { \ - if ((h) != NULL) { \ - duk_heaphdr *h__prev, *h__next; \ - h__prev = DUK_HEAPHDR_GET_PREV((heap), (h)); \ - h__next = DUK_HEAPHDR_GET_NEXT((heap), (h)); \ - DUK_ASSERT(h__prev == NULL || (DUK_HEAPHDR_GET_NEXT((heap), h__prev) == (h))); \ - DUK_ASSERT(h__next == NULL || (DUK_HEAPHDR_GET_PREV((heap), h__next) == (h))); \ - } \ - } while (0) +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_heaphdr_assert_valid_subclassed(duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_assert_links(duk_heap *heap, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_assert_valid(duk_heaphdr *h); +#define DUK_HEAPHDR_ASSERT_LINKS(heap,h) do { duk_heaphdr_assert_links((heap), (h)); } while (0) +#define DUK_HEAPHDR_ASSERT_VALID(h) do { duk_heaphdr_assert_valid((h)); } while (0) #else -#define DUK_ASSERT_HEAPHDR_LINKS(heap,h) do {} while (0) +#define DUK_HEAPHDR_ASSERT_LINKS(heap,h) do {} while (0) +#define DUK_HEAPHDR_ASSERT_VALID(h) do {} while (0) #endif +#endif /* DUK_HEAPHDR_H_INCLUDED */ +/* #include duk_refcount.h */ /* * Reference counting helper macros. The macros take a thread argument * and must thus always be executed in a specific thread context. The - * thread argument is needed for features like finalization. Currently - * it is not required for INCREF, but it is included just in case. - * - * Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not - * defined without DUK_USE_REFERENCE_COUNTING, so caller must #ifdef - * around them. + * thread argument is not really needed anymore: DECREF can operate with + * a heap pointer only, and INCREF needs neither. */ +#if !defined(DUK_REFCOUNT_H_INCLUDED) +#define DUK_REFCOUNT_H_INCLUDED + #if defined(DUK_USE_REFERENCE_COUNTING) #if defined(DUK_USE_ROM_OBJECTS) @@ -3764,6 +4791,7 @@ struct duk_heaphdr_string { DUK_ASSERT(duk__h != NULL); \ DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ } \ } while (0) #define DUK_TVAL_DECREF_FAST(thr,tv) do { \ @@ -3779,87 +4807,208 @@ struct duk_heaphdr_string { } \ } \ } while (0) +#define DUK_TVAL_DECREF_NORZ_FAST(thr,tv) do { \ + duk_tval *duk__tv = (tv); \ + DUK_ASSERT(duk__tv != NULL); \ + if (DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk__tv)) { \ + duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ + DUK_ASSERT(duk__h != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ + if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ + duk_heaphdr_refzero_norz((thr), duk__h); \ + } \ + } \ + } while (0) #define DUK_HEAPHDR_INCREF_FAST(thr,h) do { \ duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ DUK_ASSERT(duk__h != NULL); \ DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \ DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ } \ } while (0) -#define DUK_HEAPHDR_DECREF_FAST(thr,h) do { \ +#define DUK_HEAPHDR_DECREF_FAST_RAW(thr,h,rzcall,rzcast) do { \ duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ DUK_ASSERT(duk__h != NULL); \ DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \ if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ - duk_heaphdr_refzero((thr), duk__h); \ + (rzcall)((thr), (rzcast) duk__h); \ } \ } \ } while (0) +#define DUK_HEAPHDR_DECREF_FAST(thr,h) \ + DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero,duk_heaphdr *) +#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr,h) \ + DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero_norz,duk_heaphdr *) /* Slow variants, call to a helper to reduce code size. * Can be used explicitly when size is always more important than speed. */ -#define DUK_TVAL_INCREF_SLOW(thr,tv) do { \ - duk_tval_incref((tv)); \ - } while (0) -#define DUK_TVAL_DECREF_SLOW(thr,tv) do { \ - duk_tval_decref((thr), (tv)); \ - } while (0) -#define DUK_HEAPHDR_INCREF_SLOW(thr,h) do { \ - duk_heaphdr_incref((duk_heaphdr *) (h)); \ - } while (0) -#define DUK_HEAPHDR_DECREF_SLOW(thr,h) do { \ - duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ - } while (0) +#define DUK_TVAL_INCREF_SLOW(thr,tv) do { duk_tval_incref((tv)); } while (0) +#define DUK_TVAL_DECREF_SLOW(thr,tv) do { duk_tval_decref((thr), (tv)); } while (0) +#define DUK_TVAL_DECREF_NORZ_SLOW(thr,tv) do { duk_tval_decref_norz((thr), (tv)); } while (0) +#define DUK_HEAPHDR_INCREF_SLOW(thr,h) do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0) +#define DUK_HEAPHDR_DECREF_SLOW(thr,h) do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr,h) do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HSTRING_INCREF_SLOW(thr,h) do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0) +#define DUK_HSTRING_DECREF_SLOW(thr,h) do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HSTRING_DECREF_NORZ_SLOW(thr,h) do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HBUFFER_INCREF_SLOW(thr,h) do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0) +#define DUK_HBUFFER_DECREF_SLOW(thr,h) do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr,h) do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HOBJECT_INCREF_SLOW(thr,h) do { duk_heaphdr_incref((duk_heaphdr *) (h)); } while (0) +#define DUK_HOBJECT_DECREF_SLOW(thr,h) do { duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); } while (0) +#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr,h) do { duk_heaphdr_decref_norz((thr), (duk_heaphdr *) (h)); } while (0) /* Default variants. Selection depends on speed/size preference. * Concretely: with gcc 4.8.1 -Os x64 the difference in final binary * is about +1kB for _FAST variants. */ #if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +/* XXX: It would be nice to specialize for specific duk_hobject subtypes + * but current refzero queue handling prevents that. + */ #define DUK_TVAL_INCREF(thr,tv) DUK_TVAL_INCREF_FAST((thr),(tv)) #define DUK_TVAL_DECREF(thr,tv) DUK_TVAL_DECREF_FAST((thr),(tv)) +#define DUK_TVAL_DECREF_NORZ(thr,tv) DUK_TVAL_DECREF_NORZ_FAST((thr),(tv)) #define DUK_HEAPHDR_INCREF(thr,h) DUK_HEAPHDR_INCREF_FAST((thr),(h)) -#define DUK_HEAPHDR_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST((thr),(h)) +#define DUK_HEAPHDR_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero,duk_heaphdr *) +#define DUK_HEAPHDR_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_heaphdr_refzero_norz,duk_heaphdr *) +#define DUK_HSTRING_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HSTRING_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hstring_refzero,duk_hstring *) +#define DUK_HSTRING_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hstring_refzero,duk_hstring *) /* no 'norz' variant */ +#define DUK_HOBJECT_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HOBJECT_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *) +#define DUK_HOBJECT_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *) +#define DUK_HBUFFER_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HBUFFER_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hbuffer_refzero,duk_hbuffer *) +#define DUK_HBUFFER_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hbuffer_refzero,duk_hbuffer *) /* no 'norz' variant */ +#define DUK_HCOMPFUNC_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HCOMPFUNC_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *) +#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *) +#define DUK_HNATFUNC_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HNATFUNC_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *) +#define DUK_HNATFUNC_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *) +#define DUK_HBUFOBJ_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HBUFOBJ_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *) +#define DUK_HBUFOBJ_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *) +#define DUK_HTHREAD_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HTHREAD_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero,duk_hobject *) +#define DUK_HTHREAD_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_FAST_RAW((thr),(h),duk_hobject_refzero_norz,duk_hobject *) #else #define DUK_TVAL_INCREF(thr,tv) DUK_TVAL_INCREF_SLOW((thr),(tv)) #define DUK_TVAL_DECREF(thr,tv) DUK_TVAL_DECREF_SLOW((thr),(tv)) +#define DUK_TVAL_DECREF_NORZ(thr,tv) DUK_TVAL_DECREF_NORZ_SLOW((thr),(tv)) #define DUK_HEAPHDR_INCREF(thr,h) DUK_HEAPHDR_INCREF_SLOW((thr),(h)) #define DUK_HEAPHDR_DECREF(thr,h) DUK_HEAPHDR_DECREF_SLOW((thr),(h)) +#define DUK_HEAPHDR_DECREF_NORZ(thr,h) DUK_HEAPHDR_DECREF_NORZ_SLOW((thr),(h)) +#define DUK_HSTRING_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HSTRING_DECREF(thr,h) DUK_HSTRING_DECREF_SLOW((thr),(h)) +#define DUK_HSTRING_DECREF_NORZ(thr,h) DUK_HSTRING_DECREF_NORZ_SLOW((thr),(h)) +#define DUK_HOBJECT_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HOBJECT_DECREF(thr,h) DUK_HOBJECT_DECREF_SLOW((thr),(h)) +#define DUK_HOBJECT_DECREF_NORZ(thr,h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(h)) +#define DUK_HBUFFER_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HBUFFER_DECREF(thr,h) DUK_HBUFFER_DECREF_SLOW((thr),(h)) +#define DUK_HBUFFER_DECREF_NORZ(thr,h) DUK_HBUFFER_DECREF_NORZ_SLOW((thr),(h)) +#define DUK_HCOMPFUNC_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HCOMPFUNC_DECREF(thr,h) DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HNATFUNC_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HNATFUNC_DECREF(thr,h) DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HNATFUNC_DECREF_NORZ(thr,h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HBUFOBJ_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HBUFOBJ_DECREF(thr,h) DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HBUFOB_DECREF_NORZ(thr,h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HTHREAD_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HTHREAD_DECREF(thr,h) DUK_HOBJECT_DECREF_SLOW((thr),(duk_hobject *) &(h)->obj) +#define DUK_HTHREAD_DECREF_NORZ(thr,h) DUK_HOBJECT_DECREF_NORZ_SLOW((thr),(duk_hobject *) &(h)->obj) #endif -/* Casting convenience. */ -#define DUK_HSTRING_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HSTRING_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HOBJECT_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HOBJECT_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HBUFFER_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HBUFFER_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HNATIVEFUNCTION_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HNATIVEFUNCTION_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HBUFFEROBJECT_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HBUFFEROBJECT_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) - /* Convenience for some situations; the above macros don't allow NULLs - * for performance reasons. + * for performance reasons. Macros cover only actually needed cases. */ -#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do { \ +#define DUK_HEAPHDR_INCREF_ALLOWNULL(thr,h) do { \ if ((h) != NULL) { \ DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)); \ } \ } while (0) -#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do { \ +#define DUK_HEAPHDR_DECREF_ALLOWNULL(thr,h) do { \ if ((h) != NULL) { \ DUK_HEAPHDR_DECREF((thr), (duk_heaphdr *) (h)); \ } \ } while (0) +#define DUK_HEAPHDR_DECREF_NORZ_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HEAPHDR_DECREF_NORZ((thr), (duk_heaphdr *) (h)); \ + } \ + } while (0) +#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HOBJECT_INCREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HOBJECT_DECREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HOBJECT_DECREF_NORZ((thr), (h)); \ + } \ + } while (0) +#define DUK_HBUFFER_INCREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HBUFFER_INCREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HBUFFER_DECREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HBUFFER_DECREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HBUFFER_DECREF_NORZ((thr), (h)); \ + } \ + } while (0) +#define DUK_HTHREAD_INCREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HTHREAD_INCREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HTHREAD_DECREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HTHREAD_DECREF((thr), (h)); \ + } \ + } while (0) +#define DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HTHREAD_DECREF_NORZ((thr), (h)); \ + } \ + } while (0) + +/* Called after one or more DECREF NORZ calls to handle pending side effects. + * At present DECREF NORZ does freeing inline but doesn't execute finalizers, + * so these macros check for pending finalizers and execute them. The FAST + * variant is performance critical. + */ +#if defined(DUK_USE_FINALIZER_SUPPORT) +#define DUK_REFZERO_CHECK_FAST(thr) do { \ + duk_refzero_check_fast((thr)); \ + } while (0) +#define DUK_REFZERO_CHECK_SLOW(thr) do { \ + duk_refzero_check_slow((thr)); \ + } while (0) +#else /* DUK_USE_FINALIZER_SUPPORT */ +#define DUK_REFZERO_CHECK_FAST(thr) do { } while (0) +#define DUK_REFZERO_CHECK_SLOW(thr) do { } while (0) +#endif /* DUK_USE_FINALIZER_SUPPORT */ /* * Macros to set a duk_tval and update refcount of the target (decref the @@ -3874,6 +5023,13 @@ struct duk_heaphdr_string { DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) +#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0(thr,tvptr_dst) do { \ + duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ + DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ + DUK_TVAL_SET_UNDEFINED(tv__dst); \ + DUK_TVAL_DECREF_NORZ((thr), &tv__tmp); \ + } while (0) + #define DUK_TVAL_SET_UNUSED_UPDREF_ALT0(thr,tvptr_dst) do { \ duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ @@ -3904,7 +5060,7 @@ struct duk_heaphdr_string { #define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_NUMBER_CHKFAST(tv__dst, (newval)); \ + DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \ DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) #define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ @@ -3920,22 +5076,22 @@ struct duk_heaphdr_string { DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_FASTINT_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_FASTINT(tv__dst, (newval)); \ + DUK_TVAL_SET_I48(tv__dst, (newval)); \ DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) -#define DUK_TVAL_SET_FASTINT_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_FASTINT_I32(tv__dst, (newval)); \ + DUK_TVAL_SET_I32(tv__dst, (newval)); \ DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) -#define DUK_TVAL_SET_FASTINT_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; duk_tval tv__tmp; tv__dst = (tvptr_dst); \ DUK_TVAL_SET_TVAL(&tv__tmp, tv__dst); \ - DUK_TVAL_SET_FASTINT_U32(tv__dst, (newval)); \ + DUK_TVAL_SET_U32(tv__dst, (newval)); \ DUK_TVAL_DECREF((thr), &tv__tmp); /* side effects */ \ } while (0) #else @@ -4017,6 +5173,7 @@ struct duk_heaphdr_string { /* XXX: no optimized variants yet */ #define DUK_TVAL_SET_UNDEFINED_UPDREF DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 +#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ_ALT0 #define DUK_TVAL_SET_UNUSED_UPDREF DUK_TVAL_SET_UNUSED_UPDREF_ALT0 #define DUK_TVAL_SET_NULL_UPDREF DUK_TVAL_SET_NULL_UPDREF_ALT0 #define DUK_TVAL_SET_BOOLEAN_UPDREF DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0 @@ -4025,14 +5182,15 @@ struct duk_heaphdr_string { #define DUK_TVAL_SET_DOUBLE_UPDREF DUK_TVAL_SET_DOUBLE_UPDREF_ALT0 #define DUK_TVAL_SET_NAN_UPDREF DUK_TVAL_SET_NAN_UPDREF_ALT0 #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_FASTINT_UPDREF_ALT0 -#define DUK_TVAL_SET_FASTINT_I32_UPDREF DUK_TVAL_SET_FASTINT_I32_UPDREF_ALT0 -#define DUK_TVAL_SET_FASTINT_U32_UPDREF DUK_TVAL_SET_FASTINT_U32_UPDREF_ALT0 +#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_I48_UPDREF_ALT0 +#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_I32_UPDREF_ALT0 +#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_U32_UPDREF_ALT0 #else -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast int-to-double */ -#define DUK_TVAL_SET_FASTINT_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#define DUK_TVAL_SET_FASTINT_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF +#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast int-to-double */ +#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF +#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF #endif /* DUK_USE_FASTINT */ +#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_I48_UPDREF /* convenience */ #define DUK_TVAL_SET_LIGHTFUNC_UPDREF DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0 #define DUK_TVAL_SET_STRING_UPDREF DUK_TVAL_SET_STRING_UPDREF_ALT0 #define DUK_TVAL_SET_OBJECT_UPDREF DUK_TVAL_SET_OBJECT_UPDREF_ALT0 @@ -4053,34 +5211,76 @@ struct duk_heaphdr_string { #else /* DUK_USE_REFERENCE_COUNTING */ +#define DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv) 0 +#define DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(h) 0 + #define DUK_TVAL_INCREF_FAST(thr,v) do {} while (0) /* nop */ #define DUK_TVAL_DECREF_FAST(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF_NORZ_FAST(thr,v) do {} while (0) /* nop */ #define DUK_TVAL_INCREF_SLOW(thr,v) do {} while (0) /* nop */ #define DUK_TVAL_DECREF_SLOW(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF_NORZ_SLOW(thr,v) do {} while (0) /* nop */ #define DUK_TVAL_INCREF(thr,v) do {} while (0) /* nop */ #define DUK_TVAL_DECREF(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF_NORZ(thr,v) do {} while (0) /* nop */ #define DUK_HEAPHDR_INCREF_FAST(thr,h) do {} while (0) /* nop */ #define DUK_HEAPHDR_DECREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF_NORZ_FAST(thr,h) do {} while (0) /* nop */ #define DUK_HEAPHDR_INCREF_SLOW(thr,h) do {} while (0) /* nop */ #define DUK_HEAPHDR_DECREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF_NORZ_SLOW(thr,h) do {} while (0) /* nop */ #define DUK_HEAPHDR_INCREF(thr,h) do {} while (0) /* nop */ #define DUK_HEAPHDR_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF_NORZ(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_INCREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF_NORZ_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_INCREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF_NORZ_SLOW(thr,h) do {} while (0) /* nop */ #define DUK_HSTRING_INCREF(thr,h) do {} while (0) /* nop */ #define DUK_HSTRING_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF_NORZ(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_INCREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_NORZ_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_INCREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_NORZ_SLOW(thr,h) do {} while (0) /* nop */ #define DUK_HOBJECT_INCREF(thr,h) do {} while (0) /* nop */ #define DUK_HOBJECT_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_NORZ(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_INCREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_NORZ_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_INCREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_NORZ_SLOW(thr,h) do {} while (0) /* nop */ #define DUK_HBUFFER_INCREF(thr,h) do {} while (0) /* nop */ #define DUK_HBUFFER_DECREF(thr,h) do {} while (0) /* nop */ -#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) do {} while (0) /* nop */ -#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) do {} while (0) /* nop */ -#define DUK_HNATIVEFUNCTION_INCREF(thr,h) do {} while (0) /* nop */ -#define DUK_HNATIVEFUNCTION_DECREF(thr,h) do {} while (0) /* nop */ -#define DUK_HBUFFEROBJECT_INCREF(thr,h) do {} while (0) /* nop */ -#define DUK_HBUFFEROBJECT_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_NORZ(thr,h) do {} while (0) /* nop */ + +#define DUK_HCOMPFUNC_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HCOMPFUNC_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HCOMPFUNC_DECREF_NORZ(thr,h) do {} while (0) /* nop */ +#define DUK_HNATFUNC_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HNATFUNC_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HNATFUNC_DECREF_NORZ(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFOBJ_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFOBJ_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFOBJ_DECREF_NORZ(thr,h) do {} while (0) /* nop */ #define DUK_HTHREAD_INCREF(thr,h) do {} while (0) /* nop */ #define DUK_HTHREAD_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HTHREAD_DECREF_NORZ(thr,h) do {} while (0) /* nop */ #define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ #define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_INCREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr,h) do {} while (0) /* nop */ + +#define DUK_REFZERO_CHECK_FAST(thr) do {} while (0) /* nop */ +#define DUK_REFZERO_CHECK_SLOW(thr) do {} while (0) /* nop */ #define DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0(thr,tvptr_dst) do { \ duk_tval *tv__dst; tv__dst = (tvptr_dst); \ @@ -4113,7 +5313,7 @@ struct duk_heaphdr_string { } while (0) #define DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_NUMBER_CHKFAST(tv__dst, (newval)); \ + DUK_TVAL_SET_NUMBER_CHKFAST_FAST(tv__dst, (newval)); \ DUK_UNREF((thr)); \ } while (0) #define DUK_TVAL_SET_DOUBLE_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ @@ -4127,19 +5327,19 @@ struct duk_heaphdr_string { DUK_UNREF((thr)); \ } while (0) #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_FASTINT_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_I48_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_FASTINT(tv__dst, (newval)); \ + DUK_TVAL_SET_I48(tv__dst, (newval)); \ DUK_UNREF((thr)); \ } while (0) -#define DUK_TVAL_SET_FASTINT_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_I32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_FASTINT_I32(tv__dst, (newval)); \ + DUK_TVAL_SET_I32(tv__dst, (newval)); \ DUK_UNREF((thr)); \ } while (0) -#define DUK_TVAL_SET_FASTINT_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ +#define DUK_TVAL_SET_U32_UPDREF_ALT0(thr,tvptr_dst,newval) do { \ duk_tval *tv__dst; tv__dst = (tvptr_dst); \ - DUK_TVAL_SET_FASTINT_U32(tv__dst, (newval)); \ + DUK_TVAL_SET_U32(tv__dst, (newval)); \ DUK_UNREF((thr)); \ } while (0) #else @@ -4185,6 +5385,7 @@ struct duk_heaphdr_string { } while (0) #define DUK_TVAL_SET_UNDEFINED_UPDREF DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 +#define DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ DUK_TVAL_SET_UNDEFINED_UPDREF_ALT0 #define DUK_TVAL_SET_UNUSED_UPDREF DUK_TVAL_SET_UNUSED_UPDREF_ALT0 #define DUK_TVAL_SET_NULL_UPDREF DUK_TVAL_SET_NULL_UPDREF_ALT0 #define DUK_TVAL_SET_BOOLEAN_UPDREF DUK_TVAL_SET_BOOLEAN_UPDREF_ALT0 @@ -4193,14 +5394,15 @@ struct duk_heaphdr_string { #define DUK_TVAL_SET_DOUBLE_UPDREF DUK_TVAL_SET_DOUBLE_UPDREF_ALT0 #define DUK_TVAL_SET_NAN_UPDREF DUK_TVAL_SET_NAN_UPDREF_ALT0 #if defined(DUK_USE_FASTINT) -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_FASTINT_UPDREF_ALT0 -#define DUK_TVAL_SET_FASTINT_I32_UPDREF DUK_TVAL_SET_FASTINT_I32_UPDREF_ALT0 -#define DUK_TVAL_SET_FASTINT_U32_UPDREF DUK_TVAL_SET_FASTINT_U32_UPDREF_ALT0 +#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_I48_UPDREF_ALT0 +#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_I32_UPDREF_ALT0 +#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_U32_UPDREF_ALT0 #else -#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast-int-to-double */ -#define DUK_TVAL_SET_FASTINT_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF -#define DUK_TVAL_SET_FASTINT_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF +#define DUK_TVAL_SET_I48_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF /* XXX: fast-int-to-double */ +#define DUK_TVAL_SET_I32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF +#define DUK_TVAL_SET_U32_UPDREF DUK_TVAL_SET_DOUBLE_CAST_UPDREF #endif /* DUK_USE_FASTINT */ +#define DUK_TVAL_SET_FASTINT_UPDREF DUK_TVAL_SET_I48_UPDREF /* convenience */ #define DUK_TVAL_SET_LIGHTFUNC_UPDREF DUK_TVAL_SET_LIGHTFUNC_UPDREF_ALT0 #define DUK_TVAL_SET_STRING_UPDREF DUK_TVAL_SET_STRING_UPDREF_ALT0 #define DUK_TVAL_SET_OBJECT_UPDREF DUK_TVAL_SET_OBJECT_UPDREF_ALT0 @@ -4213,16 +5415,99 @@ struct duk_heaphdr_string { #endif /* DUK_USE_REFERENCE_COUNTING */ -#endif /* DUK_HEAPHDR_H_INCLUDED */ -#line 1 "duk_api_internal.h" +/* + * Some convenience macros that don't have optimized implementations now. + */ + +#define DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr,tv_dst,tv_src) do { \ + duk_hthread *duk__thr = (thr); \ + duk_tval *duk__dst = (tv_dst); \ + duk_tval *duk__src = (tv_src); \ + DUK_UNREF(duk__thr); \ + DUK_TVAL_DECREF_NORZ(thr, duk__dst); \ + DUK_TVAL_SET_TVAL(duk__dst, duk__src); \ + DUK_TVAL_INCREF(thr, duk__dst); \ + } while (0) + +#define DUK_TVAL_SET_U32_UPDREF_NORZ(thr,tv_dst,val) do { \ + duk_hthread *duk__thr = (thr); \ + duk_tval *duk__dst = (tv_dst); \ + duk_uint32_t duk__val = (duk_uint32_t) (val); \ + DUK_UNREF(duk__thr); \ + DUK_TVAL_DECREF_NORZ(thr, duk__dst); \ + DUK_TVAL_SET_U32(duk__dst, duk__val); \ + } while (0) + +/* + * Prototypes + */ + +#if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL_DECL void duk_refzero_check_slow(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_refzero_check_fast(duk_hthread *thr); +#endif +DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr); +DUK_INTERNAL_DECL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h); +#if 0 /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */ +DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h); +DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h); +DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h); +DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h); +DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h); +DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h); +#endif +DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h); +#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h); /* no 'norz' variant */ +DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h); /* no 'norz' variant */ +DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h); +DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h); +#else +DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); +DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h); +#endif +#else /* DUK_USE_REFERENCE_COUNTING */ +/* no refcounting */ +#endif /* DUK_USE_REFERENCE_COUNTING */ + +#endif /* DUK_REFCOUNT_H_INCLUDED */ +/* #include duk_api_internal.h */ /* * Internal API calls which have (stack and other) semantics similar * to the public API. */ -#ifndef DUK_API_INTERNAL_H_INCLUDED +#if !defined(DUK_API_INTERNAL_H_INCLUDED) #define DUK_API_INTERNAL_H_INCLUDED +/* Inline macro helpers. */ +#if defined(DUK_USE_PREFER_SIZE) +#define DUK_INLINE_PERF +#define DUK_ALWAYS_INLINE_PERF +#define DUK_NOINLINE_PERF +#else +#define DUK_INLINE_PERF DUK_INLINE +#define DUK_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE +#define DUK_NOINLINE_PERF DUK_NOINLINE +#endif + +/* Inline macro helpers, for bytecode executor. */ +#if defined(DUK_USE_EXEC_PREFER_SIZE) +#define DUK_EXEC_INLINE_PERF +#define DUK_EXEC_ALWAYS_INLINE_PERF +#define DUK_EXEC_NOINLINE_PERF +#else +#define DUK_EXEC_INLINE_PERF DUK_INLINE +#define DUK_EXEC_ALWAYS_INLINE_PERF DUK_ALWAYS_INLINE +#define DUK_EXEC_NOINLINE_PERF DUK_NOINLINE +#endif + /* duk_push_sprintf constants */ #define DUK_PUSH_SPRINTF_INITIAL_SIZE 256L #define DUK_PUSH_SPRINTF_SANITY_LIMIT (1L * 1024L * 1024L * 1024L) @@ -4232,184 +5517,354 @@ struct duk_heaphdr_string { */ #define DUK_ERRCODE_FLAG_NOBLAME_FILELINE (1L << 24) -/* Valstack resize flags */ -#define DUK_VSRESIZE_FLAG_SHRINK (1 << 0) -#define DUK_VSRESIZE_FLAG_COMPACT (1 << 1) -#define DUK_VSRESIZE_FLAG_THROW (1 << 2) - /* Current convention is to use duk_size_t for value stack sizes and global indices, * and duk_idx_t for local frame indices. */ -DUK_INTERNAL_DECL -duk_bool_t duk_valstack_resize_raw(duk_context *ctx, - duk_size_t min_new_size, - duk_small_uint_t flags); +DUK_INTERNAL_DECL void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes); +DUK_INTERNAL_DECL duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes); +DUK_INTERNAL_DECL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug); + +DUK_INTERNAL_DECL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count); + +DUK_INTERNAL_DECL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count); + +DUK_INTERNAL_DECL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start); + +DUK_INTERNAL_DECL void duk_dup_0(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_dup_1(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_dup_2(duk_hthread *thr); +/* duk_dup_m1() would be same as duk_dup_top() */ +DUK_INTERNAL_DECL void duk_dup_m2(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_dup_m3(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_dup_m4(duk_hthread *thr); + +DUK_INTERNAL_DECL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL void duk_remove_m2(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); +DUK_INTERNAL_DECL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); + +DUK_INTERNAL_DECL duk_int_t duk_get_type_tval(duk_tval *tv); +DUK_INTERNAL_DECL duk_uint_t duk_get_type_mask_tval(duk_tval *tv); #if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL_DECL const char *duk_get_type_name(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx); #endif +DUK_INTERNAL_DECL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL void duk_push_tval(duk_context *ctx, duk_tval *tv); +DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL void duk_push_tval(duk_hthread *thr, duk_tval *tv); /* Push the current 'this' binding; throw TypeError if binding is not object * coercible (CheckObjectCoercible). */ -DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_context *ctx); +DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_hthread *thr); /* duk_push_this() + CheckObjectCoercible() + duk_to_object() */ -DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx); +DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr); /* duk_push_this() + CheckObjectCoercible() + duk_to_string() */ -DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx); +DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr); + +DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i); /* Get a borrowed duk_tval pointer to the current 'this' binding. Caller must * make sure there's an active callstack entry. Note that the returned pointer * is unstable with regards to side effects. */ -DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx); +DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr); /* XXX: add fastint support? */ -#define duk_push_u64(ctx,val) \ - duk_push_number((ctx), (duk_double_t) (val)) -#define duk_push_i64(ctx,val) \ - duk_push_number((ctx), (duk_double_t) (val)) +#define duk_push_u64(thr,val) \ + duk_push_number((thr), (duk_double_t) (val)) +#define duk_push_i64(thr,val) \ + duk_push_number((thr), (duk_double_t) (val)) /* duk_push_(u)int() is guaranteed to support at least (un)signed 32-bit range */ -#define duk_push_u32(ctx,val) \ - duk_push_uint((ctx), (duk_uint_t) (val)) -#define duk_push_i32(ctx,val) \ - duk_push_int((ctx), (duk_int_t) (val)) +#define duk_push_u32(thr,val) \ + duk_push_uint((thr), (duk_uint_t) (val)) +#define duk_push_i32(thr,val) \ + duk_push_int((thr), (duk_int_t) (val)) /* sometimes stack and array indices need to go on the stack */ -#define duk_push_idx(ctx,val) \ - duk_push_int((ctx), (duk_int_t) (val)) -#define duk_push_uarridx(ctx,val) \ - duk_push_uint((ctx), (duk_uint_t) (val)) -#define duk_push_size_t(ctx,val) \ - duk_push_uint((ctx), (duk_uint_t) (val)) /* XXX: assumed to fit for now */ +#define duk_push_idx(thr,val) \ + duk_push_int((thr), (duk_int_t) (val)) +#define duk_push_uarridx(thr,val) \ + duk_push_uint((thr), (duk_uint_t) (val)) +#define duk_push_size_t(thr,val) \ + duk_push_uint((thr), (duk_uint_t) (val)) /* XXX: assumed to fit for now */ -DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hcompiledfunction *duk_get_hcompiledfunction(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hnativefunction *duk_get_hnativefunction(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum); +DUK_INTERNAL_DECL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv); -#if 0 /* This would be pointless: unexpected type and lightfunc would both return NULL */ -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_or_lfunc(duk_context *ctx, duk_idx_t index); -#endif -DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx); + +DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx); + +DUK_INTERNAL_DECL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len, duk_bool_t throw_flag, duk_bool_t *out_isbuffer); + +DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum); + +DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); +DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); +DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask); +#define duk_require_hobject_promote_lfunc(thr,idx) \ + duk_require_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC) +#define duk_get_hobject_promote_lfunc(thr,idx) \ + duk_get_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC) #if 0 /*unused*/ -DUK_INTERNAL_DECL void *duk_get_voidptr(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx); #endif -DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx); + +DUK_INTERNAL_DECL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv); + +DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_m1(duk_hthread *thr); +DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx); + +DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx); + +DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr); +DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr); + +DUK_INTERNAL_DECL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr); + #if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */ -DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t index); -#endif -DUK_INTERNAL_DECL void duk_to_object_class_string_top(duk_context *ctx); -#if !defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL_DECL void duk_push_hobject_class_string(duk_context *ctx, duk_hobject *h); +DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx); #endif +DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects); -DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped); /* out_clamped=NULL, RangeError if outside range */ -DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval); -DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval); +DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped); /* out_clamped=NULL, RangeError if outside range */ +DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval); +DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx); #endif +DUK_INTERNAL_DECL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hcompiledfunction *duk_require_hcompiledfunction(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hnativefunction *duk_require_hnativefunction(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len); +DUK_INTERNAL_DECL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum); +DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index); +DUK_INTERNAL_DECL void duk_push_hstring(duk_hthread *thr, duk_hstring *h); +DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx); +DUK_INTERNAL_DECL void duk_push_hstring_empty(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_push_hobject(duk_hthread *thr, duk_hobject *h); +DUK_INTERNAL_DECL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h); +#define duk_push_hthread(thr,h) \ + duk_push_hobject((thr), (duk_hobject *) (h)) +#define duk_push_hnatfunc(thr,h) \ + duk_push_hobject((thr), (duk_hobject *) (h)) +DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx); +DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx); +DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto); +DUK_INTERNAL_DECL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr); +DUK_INTERNAL_DECL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs); +DUK_INTERNAL_DECL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs); -DUK_INTERNAL_DECL void duk_push_hstring(duk_context *ctx, duk_hstring *h); -DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_context *ctx, duk_small_int_t stridx); -DUK_INTERNAL_DECL void duk_push_hobject(duk_context *ctx, duk_hobject *h); -DUK_INTERNAL_DECL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h); -#define duk_push_hthread(ctx,h) \ - duk_push_hobject((ctx), (duk_hobject *) (h)) -#define duk_push_hcompiledfunction(ctx,h) \ - duk_push_hobject((ctx), (duk_hobject *) (h)) -#define duk_push_hnativefunction(ctx,h) \ - duk_push_hobject((ctx), (duk_hobject *) (h)) -DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx); -DUK_INTERNAL_DECL duk_idx_t duk_push_object_helper(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx); -DUK_INTERNAL_DECL duk_idx_t duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto); -DUK_INTERNAL_DECL duk_idx_t duk_push_object_internal(duk_context *ctx); -DUK_INTERNAL_DECL duk_idx_t duk_push_compiledfunction(duk_context *ctx); -DUK_INTERNAL_DECL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs); -DUK_INTERNAL_DECL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs); - -DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz); -DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv); -DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv); -DUK_INTERNAL_DECL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx); - -#if !defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_context *ctx, duk_idx_t index); -DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_context *ctx, duk_tval *tv); -#endif - -DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [val] */ -DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [val] -> [] */ -DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [] */ -DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx); /* [] -> [] */ - -DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_bool_t *out_has_prop); /* [] -> [] */ - -DUK_INTERNAL_DECL void duk_xdef_prop(duk_context *ctx, duk_idx_t obj_index, duk_small_uint_t desc_flags); /* [key val] -> [] */ -DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index, duk_small_uint_t desc_flags); /* [val] -> [] */ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags); /* [val] -> [] */ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags); /* [] -> [] */ -DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags); /* [] -> [] */ - -/* These are macros for now, but could be separate functions to reduce code - * footprint (check call site count before refactoring). +/* XXX: duk_push_harray() and duk_push_hcompfunc() are inconsistent with + * duk_push_hobject() etc which don't create a new value. */ -#define duk_xdef_prop_wec(ctx,obj_index) \ - duk_xdef_prop((ctx), (obj_index), DUK_PROPDESC_FLAGS_WEC) -#define duk_xdef_prop_index_wec(ctx,obj_index,arr_index) \ - duk_xdef_prop_index((ctx), (obj_index), (arr_index), DUK_PROPDESC_FLAGS_WEC) -#define duk_xdef_prop_stridx_wec(ctx,obj_index,stridx) \ - duk_xdef_prop_stridx((ctx), (obj_index), (stridx), DUK_PROPDESC_FLAGS_WEC) +DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_hthread *thr); +DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size); +DUK_INTERNAL_DECL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size); -/* Set object 'length'. */ -DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t length); +DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz); +DUK_INTERNAL_DECL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags); +DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv); +#if 0 /* not used yet */ +DUK_INTERNAL_DECL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h); +#endif +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL_DECL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx); +#endif + +DUK_INTERNAL_DECL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len); +DUK_INTERNAL_DECL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len); + +DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv); + +/* The duk_xxx_prop_stridx_short() variants expect their arguments to be short + * enough to be packed into a single 32-bit integer argument. Argument limits + * vary per call; typically 16 bits are assigned to the signed value stack index + * and the stridx. In practice these work well for footprint with constant + * arguments and such call sites are also easiest to verify to be correct. + */ + +DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [val] */ +DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_get_prop_stridx_short(thr,obj_idx,stridx) \ + (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ + DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ + duk_get_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) +DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop); /* [] -> [] */ + +DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx); +DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); +DUK_INTERNAL_DECL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_xget_owndataprop_stridx_short(thr,obj_idx,stridx) \ + (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ + DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ + duk_xget_owndataprop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) + +DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [val] -> [] */ +DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_put_prop_stridx_short(thr,obj_idx,stridx) \ + (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \ + DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ + duk_put_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) + +DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ +#if 0 /* Too few call sites to be useful. */ +DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \ + (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \ + DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \ + duk_del_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) +#endif +#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \ + duk_del_prop_stridx((thr), (obj_idx), (stridx)) + +DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ +#if 0 /* Too few call sites to be useful. */ +DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \ + (DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \ + DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \ + duk_has_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx)))) +#endif +#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \ + duk_has_prop_stridx((thr), (obj_idx), (stridx)) + +DUK_INTERNAL_DECL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags); /* [key val] -> [] */ + +DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags); /* [val] -> [] */ + +/* XXX: Because stridx and desc_flags have a limited range, this call could + * always pack stridx and desc_flags into a single argument. + */ +DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags); /* [val] -> [] */ +DUK_INTERNAL_DECL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args); +#define duk_xdef_prop_stridx_short(thr,obj_idx,stridx,desc_flags) \ + (DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x80L && (duk_int_t) (obj_idx) <= 0x7fL), \ + DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \ + DUK_ASSERT_EXPR((duk_int_t) (desc_flags) >= 0 && (duk_int_t) (desc_flags) <= 0xffL), \ + duk_xdef_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 24) + (((duk_uint_t) (stridx)) << 8) + (duk_uint_t) (desc_flags))) + +#define duk_xdef_prop_wec(thr,obj_idx) \ + duk_xdef_prop((thr), (obj_idx), DUK_PROPDESC_FLAGS_WEC) +#define duk_xdef_prop_index_wec(thr,obj_idx,arr_idx) \ + duk_xdef_prop_index((thr), (obj_idx), (arr_idx), DUK_PROPDESC_FLAGS_WEC) +#define duk_xdef_prop_stridx_wec(thr,obj_idx,stridx) \ + duk_xdef_prop_stridx((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC) +#define duk_xdef_prop_stridx_short_wec(thr,obj_idx,stridx) \ + duk_xdef_prop_stridx_short((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC) + +#if 0 /*unused*/ +DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags); /* [] -> [] */ +#endif + +DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ + +DUK_INTERNAL_DECL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx); + +DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count); +DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx); +#if 0 +DUK_INTERNAL_DECL void duk_unpack(duk_hthread *thr); +#endif + +DUK_INTERNAL_DECL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h); + +DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_hthread *thr); + +DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top); +DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr); + +DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count); +DUK_INTERNAL_DECL void duk_pop_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_2_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_3_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count); +DUK_INTERNAL_DECL void duk_pop_nodecref_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_2_nodecref_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_3_nodecref_unsafe(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_pop_undefined(duk_hthread *thr); + +DUK_INTERNAL_DECL void duk_compact_m1(duk_hthread *thr); + +DUK_INTERNAL_DECL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze); + +DUK_INTERNAL_DECL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx); +DUK_INTERNAL_DECL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count); + +DUK_INTERNAL_DECL void duk_concat_2(duk_hthread *thr); + +DUK_INTERNAL_DECL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags); + +#if defined(DUK_USE_SYMBOL_BUILTIN) +DUK_INTERNAL_DECL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint); +#endif + +DUK_INTERNAL_DECL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx); /* Raw internal valstack access macros: access is unsafe so call site * must have a guarantee that the index is valid. When that is the case, * using these macro results in faster and smaller code than duk_get_tval(). * Both 'ctx' and 'idx' are evaluted multiple times, but only for asserts. */ -#define DUK_ASSERT_VALID_NEGIDX(ctx,idx) \ - (DUK_ASSERT_EXPR((idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx)))) -#define DUK_ASSERT_VALID_POSIDX(ctx,idx) \ - (DUK_ASSERT_EXPR((idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx)))) -#define DUK_GET_TVAL_NEGIDX(ctx,idx) \ - (DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_top + (idx)) -#define DUK_GET_TVAL_POSIDX(ctx,idx) \ - (DUK_ASSERT_VALID_POSIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_bottom + (idx)) -#define DUK_GET_HOBJECT_NEGIDX(ctx,idx) \ - (DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_top + (idx))) -#define DUK_GET_HOBJECT_POSIDX(ctx,idx) \ - (DUK_ASSERT_VALID_POSIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_bottom + (idx))) +#define DUK_ASSERT_VALID_NEGIDX(thr,idx) \ + (DUK_ASSERT_EXPR((duk_int_t) (idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx)))) +#define DUK_ASSERT_VALID_POSIDX(thr,idx) \ + (DUK_ASSERT_EXPR((duk_int_t) (idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx)))) +#define DUK_GET_TVAL_NEGIDX(thr,idx) \ + (DUK_ASSERT_VALID_NEGIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_top + (idx)) +#define DUK_GET_TVAL_POSIDX(thr,idx) \ + (DUK_ASSERT_VALID_POSIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_bottom + (idx)) +#define DUK_GET_HOBJECT_NEGIDX(thr,idx) \ + (DUK_ASSERT_VALID_NEGIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_top + (idx))) +#define DUK_GET_HOBJECT_POSIDX(thr,idx) \ + (DUK_ASSERT_VALID_POSIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_bottom + (idx))) + +#define DUK_GET_THIS_TVAL_PTR(thr) \ + (DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \ + (thr)->valstack_bottom - 1) + +DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr); +DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr); +DUK_INTERNAL_DECL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr); #endif /* DUK_API_INTERNAL_H_INCLUDED */ -#line 1 "duk_hstring.h" +/* #include duk_hstring.h */ /* * Heap string representation. * @@ -4420,13 +5875,13 @@ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_siz * strings used as internal property names and raw buffers converted to * strings. In such cases the 'clen' field contains an inaccurate value. * - * Ecmascript requires support for 32-bit long strings. However, since each + * ECMAScript requires support for 32-bit long strings. However, since each * 16-bit codepoint can take 3 bytes in CESU-8, this representation can only * support about 1.4G codepoint long strings in extreme cases. This is not * really a practical issue. */ -#ifndef DUK_HSTRING_H_INCLUDED +#if !defined(DUK_HSTRING_H_INCLUDED) #define DUK_HSTRING_H_INCLUDED /* Impose a maximum string length for now. Restricted artificially to @@ -4444,50 +5899,61 @@ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_siz #define DUK_HSTRING_MAX_BYTELEN (0x7fffffffUL) #endif -/* XXX: could add flags for "is valid CESU-8" (Ecmascript compatible strings), +/* XXX: could add flags for "is valid CESU-8" (ECMAScript compatible strings), * "is valid UTF-8", "is valid extended UTF-8" (internal strings are not, * regexp bytecode is), and "contains non-BMP characters". These are not * needed right now. */ +/* With lowmem builds the high 16 bits of duk_heaphdr are used for other + * purposes, so this leaves 7 duk_heaphdr flags and 9 duk_hstring flags. + */ #define DUK_HSTRING_FLAG_ASCII DUK_HEAPHDR_USER_FLAG(0) /* string is ASCII, clen == blen */ #define DUK_HSTRING_FLAG_ARRIDX DUK_HEAPHDR_USER_FLAG(1) /* string is a valid array index */ -#define DUK_HSTRING_FLAG_INTERNAL DUK_HEAPHDR_USER_FLAG(2) /* string is internal */ -#define DUK_HSTRING_FLAG_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(3) /* string is a reserved word (non-strict) */ -#define DUK_HSTRING_FLAG_STRICT_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(4) /* string is a reserved word (strict) */ -#define DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS DUK_HEAPHDR_USER_FLAG(5) /* string is 'eval' or 'arguments' */ -#define DUK_HSTRING_FLAG_EXTDATA DUK_HEAPHDR_USER_FLAG(6) /* string data is external (duk_hstring_external) */ +#define DUK_HSTRING_FLAG_SYMBOL DUK_HEAPHDR_USER_FLAG(2) /* string is a symbol (invalid utf-8) */ +#define DUK_HSTRING_FLAG_HIDDEN DUK_HEAPHDR_USER_FLAG(3) /* string is a hidden symbol (implies symbol, Duktape 1.x internal string) */ +#define DUK_HSTRING_FLAG_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(4) /* string is a reserved word (non-strict) */ +#define DUK_HSTRING_FLAG_STRICT_RESERVED_WORD DUK_HEAPHDR_USER_FLAG(5) /* string is a reserved word (strict) */ +#define DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS DUK_HEAPHDR_USER_FLAG(6) /* string is 'eval' or 'arguments' */ +#define DUK_HSTRING_FLAG_EXTDATA DUK_HEAPHDR_USER_FLAG(7) /* string data is external (duk_hstring_external) */ +#define DUK_HSTRING_FLAG_PINNED_LITERAL DUK_HEAPHDR_USER_FLAG(8) /* string is a literal, and pinned */ #define DUK_HSTRING_HAS_ASCII(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) #define DUK_HSTRING_HAS_ARRIDX(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_HAS_INTERNAL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_INTERNAL) +#define DUK_HSTRING_HAS_SYMBOL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) +#define DUK_HSTRING_HAS_HIDDEN(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) #define DUK_HSTRING_HAS_RESERVED_WORD(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) #define DUK_HSTRING_HAS_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) #define DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) #define DUK_HSTRING_HAS_EXTDATA(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) +#define DUK_HSTRING_HAS_PINNED_LITERAL(x) DUK_HEAPHDR_CHECK_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) #define DUK_HSTRING_SET_ASCII(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) #define DUK_HSTRING_SET_ARRIDX(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_SET_INTERNAL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_INTERNAL) +#define DUK_HSTRING_SET_SYMBOL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) +#define DUK_HSTRING_SET_HIDDEN(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) #define DUK_HSTRING_SET_RESERVED_WORD(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) #define DUK_HSTRING_SET_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) #define DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) #define DUK_HSTRING_SET_EXTDATA(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) +#define DUK_HSTRING_SET_PINNED_LITERAL(x) DUK_HEAPHDR_SET_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) #define DUK_HSTRING_CLEAR_ASCII(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ASCII) #define DUK_HSTRING_CLEAR_ARRIDX(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_ARRIDX) -#define DUK_HSTRING_CLEAR_INTERNAL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_INTERNAL) +#define DUK_HSTRING_CLEAR_SYMBOL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_SYMBOL) +#define DUK_HSTRING_CLEAR_HIDDEN(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_HIDDEN) #define DUK_HSTRING_CLEAR_RESERVED_WORD(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_RESERVED_WORD) #define DUK_HSTRING_CLEAR_STRICT_RESERVED_WORD(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_STRICT_RESERVED_WORD) #define DUK_HSTRING_CLEAR_EVAL_OR_ARGUMENTS(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EVAL_OR_ARGUMENTS) #define DUK_HSTRING_CLEAR_EXTDATA(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_EXTDATA) +#define DUK_HSTRING_CLEAR_PINNED_LITERAL(x) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(x)->hdr, DUK_HSTRING_FLAG_PINNED_LITERAL) #if 0 /* Slightly smaller code without explicit flag, but explicit flag * is very useful when 'clen' is dropped. */ #define DUK_HSTRING_IS_ASCII(x) (DUK_HSTRING_GET_BYTELEN((x)) == DUK_HSTRING_GET_CHARLEN((x))) #endif -#define DUK_HSTRING_IS_ASCII(x) DUK_HSTRING_HAS_ASCII((x)) +#define DUK_HSTRING_IS_ASCII(x) DUK_HSTRING_HAS_ASCII((x)) /* lazily set! */ #define DUK_HSTRING_IS_EMPTY(x) (DUK_HSTRING_GET_BYTELEN((x)) == 0) #if defined(DUK_USE_STRHASH16) @@ -4508,7 +5974,7 @@ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_siz (x)->hdr.h_strextra16 = (v); \ } while (0) #if defined(DUK_USE_HSTRING_CLEN) -#define DUK_HSTRING_GET_CHARLEN(x) ((x)->clen16) +#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) #define DUK_HSTRING_SET_CHARLEN(x,v) do { \ (x)->clen16 = (v); \ } while (0) @@ -4523,7 +5989,7 @@ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_siz #define DUK_HSTRING_SET_BYTELEN(x,v) do { \ (x)->blen = (v); \ } while (0) -#define DUK_HSTRING_GET_CHARLEN(x) ((x)->clen) +#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) #define DUK_HSTRING_SET_CHARLEN(x,v) do { \ (x)->clen = (v); \ } while (0) @@ -4543,18 +6009,39 @@ DUK_INTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_siz #define DUK_HSTRING_GET_DATA_END(x) \ (DUK_HSTRING_GET_DATA((x)) + (x)->blen) -/* marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest valid) */ +/* Marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest + * valid). + */ #define DUK_HSTRING_NO_ARRAY_INDEX (0xffffffffUL) -/* get array index related to string (or return DUK_HSTRING_NO_ARRAY_INDEX); +#if defined(DUK_USE_HSTRING_ARRIDX) +#define DUK_HSTRING_GET_ARRIDX_FAST(h) ((h)->arridx) +#define DUK_HSTRING_GET_ARRIDX_SLOW(h) ((h)->arridx) +#else +/* Get array index related to string (or return DUK_HSTRING_NO_ARRAY_INDEX); * avoids helper call if string has no array index value. */ #define DUK_HSTRING_GET_ARRIDX_FAST(h) \ - (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_string_helper((h)) : DUK_HSTRING_NO_ARRAY_INDEX) + (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_hstring_fast_known((h)) : DUK_HSTRING_NO_ARRAY_INDEX) -/* slower but more compact variant */ +/* Slower but more compact variant. */ #define DUK_HSTRING_GET_ARRIDX_SLOW(h) \ - (duk_js_to_arrayindex_string_helper((h))) + (duk_js_to_arrayindex_hstring_fast((h))) +#endif + +/* XXX: these actually fit into duk_hstring */ +#define DUK_SYMBOL_TYPE_HIDDEN 0 +#define DUK_SYMBOL_TYPE_GLOBAL 1 +#define DUK_SYMBOL_TYPE_LOCAL 2 +#define DUK_SYMBOL_TYPE_WELLKNOWN 3 + +/* Assertion for duk_hstring validity. */ +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hstring_assert_valid(duk_hstring *h); +#define DUK_HSTRING_ASSERT_VALID(h) do { duk_hstring_assert_valid((h)); } while (0) +#else +#define DUK_HSTRING_ASSERT_VALID(h) do {} while (0) +#endif /* * Misc @@ -4568,25 +6055,26 @@ struct duk_hstring { */ duk_heaphdr_string hdr; - /* Note: we could try to stuff a partial hash (e.g. 16 bits) into the - * shared heap header. Good hashing needs more hash bits though. - */ - - /* string hash */ + /* String hash. */ #if defined(DUK_USE_STRHASH16) /* If 16-bit hash is in use, stuff it into duk_heaphdr_string flags. */ #else duk_uint32_t hash; #endif - /* length in bytes (not counting NUL term) */ + /* Precomputed array index (or DUK_HSTRING_NO_ARRAY_INDEX). */ +#if defined(DUK_USE_HSTRING_ARRIDX) + duk_uarridx_t arridx; +#endif + + /* Length in bytes (not counting NUL term). */ #if defined(DUK_USE_STRLEN16) /* placed in duk_heaphdr_string */ #else duk_uint32_t blen; #endif - /* length in codepoints (must be E5 compatible) */ + /* Length in codepoints (must be E5 compatible). */ #if defined(DUK_USE_STRLEN16) #if defined(DUK_USE_HSTRING_CLEN) duk_uint16_t clen16; @@ -4598,7 +6086,7 @@ struct duk_hstring { #endif /* - * String value of 'blen+1' bytes follows (+1 for NUL termination + * String data of 'blen+1' bytes follows (+1 for NUL termination * convenience for C API). No alignment needs to be guaranteed * for strings, but fields above should guarantee alignment-by-4 * (but not alignment-by-8). @@ -4622,23 +6110,24 @@ struct duk_hstring_external { * Prototypes */ -DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos); - -#if !defined(DUK_USE_HSTRING_CLEN) +DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos, duk_bool_t surrogate_aware); +DUK_INTERNAL_DECL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr); DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); +#if !defined(DUK_USE_HSTRING_LAZY_CLEN) +DUK_INTERNAL_DECL void duk_hstring_init_charlen(duk_hstring *h); #endif #endif /* DUK_HSTRING_H_INCLUDED */ -#line 1 "duk_hobject.h" +/* #include duk_hobject.h */ /* * Heap object representation. * - * Heap objects are used for Ecmascript objects, arrays, and functions, + * Heap objects are used for ECMAScript objects, arrays, and functions, * but also for internal control like declarative and object environment * records. Compiled functions, native functions, and threads are also * objects but with an extended C struct. * - * Objects provide the required Ecmascript semantics and exotic behaviors + * Objects provide the required ECMAScript semantics and exotic behaviors * especially for property access. * * Properties are stored in three conceptual parts: @@ -4661,31 +6150,36 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); * parts are resized together, and makes property access a bit complicated. */ -#ifndef DUK_HOBJECT_H_INCLUDED +#if !defined(DUK_HOBJECT_H_INCLUDED) #define DUK_HOBJECT_H_INCLUDED -/* Object flag. There are currently 26 flag bits available. Make sure - * this stays in sync with debugger object inspection code. +/* Object flags. Make sure this stays in sync with debugger object + * inspection code. + */ + +/* XXX: some flags are object subtype specific (e.g. common to all function + * subtypes, duk_harray, etc) and could be reused for different subtypes. */ #define DUK_HOBJECT_FLAG_EXTENSIBLE DUK_HEAPHDR_USER_FLAG(0) /* object is extensible */ #define DUK_HOBJECT_FLAG_CONSTRUCTABLE DUK_HEAPHDR_USER_FLAG(1) /* object is constructable */ -#define DUK_HOBJECT_FLAG_BOUND DUK_HEAPHDR_USER_FLAG(2) /* object established using Function.prototype.bind() */ -#define DUK_HOBJECT_FLAG_COMPILEDFUNCTION DUK_HEAPHDR_USER_FLAG(4) /* object is a compiled function (duk_hcompiledfunction) */ -#define DUK_HOBJECT_FLAG_NATIVEFUNCTION DUK_HEAPHDR_USER_FLAG(5) /* object is a native function (duk_hnativefunction) */ -#define DUK_HOBJECT_FLAG_BUFFEROBJECT DUK_HEAPHDR_USER_FLAG(6) /* object is a buffer object (duk_hbufferobject) (always exotic) */ -#define DUK_HOBJECT_FLAG_THREAD DUK_HEAPHDR_USER_FLAG(7) /* object is a thread (duk_hthread) */ +#define DUK_HOBJECT_FLAG_CALLABLE DUK_HEAPHDR_USER_FLAG(2) /* object is callable */ +#define DUK_HOBJECT_FLAG_BOUNDFUNC DUK_HEAPHDR_USER_FLAG(3) /* object established using Function.prototype.bind() */ +#define DUK_HOBJECT_FLAG_COMPFUNC DUK_HEAPHDR_USER_FLAG(4) /* object is a compiled function (duk_hcompfunc) */ +#define DUK_HOBJECT_FLAG_NATFUNC DUK_HEAPHDR_USER_FLAG(5) /* object is a native function (duk_hnatfunc) */ +#define DUK_HOBJECT_FLAG_BUFOBJ DUK_HEAPHDR_USER_FLAG(6) /* object is a buffer object (duk_hbufobj) (always exotic) */ +#define DUK_HOBJECT_FLAG_FASTREFS DUK_HEAPHDR_USER_FLAG(7) /* object has no fields needing DECREF/marking beyond base duk_hobject header */ #define DUK_HOBJECT_FLAG_ARRAY_PART DUK_HEAPHDR_USER_FLAG(8) /* object has an array part (a_size may still be 0) */ #define DUK_HOBJECT_FLAG_STRICT DUK_HEAPHDR_USER_FLAG(9) /* function: function object is strict */ #define DUK_HOBJECT_FLAG_NOTAIL DUK_HEAPHDR_USER_FLAG(10) /* function: function must not be tail called */ -#define DUK_HOBJECT_FLAG_NEWENV DUK_HEAPHDR_USER_FLAG(11) /* function: create new environment when called (see duk_hcompiledfunction) */ +#define DUK_HOBJECT_FLAG_NEWENV DUK_HEAPHDR_USER_FLAG(11) /* function: create new environment when called (see duk_hcompfunc) */ #define DUK_HOBJECT_FLAG_NAMEBINDING DUK_HEAPHDR_USER_FLAG(12) /* function: create binding for func name (function templates only, used for named function expressions) */ #define DUK_HOBJECT_FLAG_CREATEARGS DUK_HEAPHDR_USER_FLAG(13) /* function: create an arguments object on function call */ -#define DUK_HOBJECT_FLAG_ENVRECCLOSED DUK_HEAPHDR_USER_FLAG(14) /* envrec: (declarative) record is closed */ +#define DUK_HOBJECT_FLAG_HAVE_FINALIZER DUK_HEAPHDR_USER_FLAG(14) /* object has a callable (own) finalizer property */ #define DUK_HOBJECT_FLAG_EXOTIC_ARRAY DUK_HEAPHDR_USER_FLAG(15) /* 'Array' object, array length and index exotic behavior */ #define DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ DUK_HEAPHDR_USER_FLAG(16) /* 'String' object, array index exotic behavior */ #define DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS DUK_HEAPHDR_USER_FLAG(17) /* 'Arguments' object and has arguments exotic behavior (non-strict callee) */ -#define DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC DUK_HEAPHDR_USER_FLAG(18) /* Duktape/C (nativefunction) object, exotic 'length' */ -#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ DUK_HEAPHDR_USER_FLAG(19) /* 'Proxy' object */ +#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ DUK_HEAPHDR_USER_FLAG(18) /* 'Proxy' object */ +#define DUK_HOBJECT_FLAG_SPECIAL_CALL DUK_HEAPHDR_USER_FLAG(19) /* special casing in call behavior, for .call(), .apply(), etc. */ #define DUK_HOBJECT_FLAG_CLASS_BASE DUK_HEAPHDR_USER_FLAG_NUMBER(20) #define DUK_HOBJECT_FLAG_CLASS_BITS 5 @@ -4706,26 +6200,27 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_CLASS_AS_FLAGS(v) (((duk_uint_t) (v)) << DUK_HOBJECT_FLAG_CLASS_BASE) /* E5 Section 8.6.2 + custom classes */ -#define DUK_HOBJECT_CLASS_UNUSED 0 -#define DUK_HOBJECT_CLASS_ARGUMENTS 1 +#define DUK_HOBJECT_CLASS_NONE 0 +#define DUK_HOBJECT_CLASS_OBJECT 1 #define DUK_HOBJECT_CLASS_ARRAY 2 -#define DUK_HOBJECT_CLASS_BOOLEAN 3 -#define DUK_HOBJECT_CLASS_DATE 4 -#define DUK_HOBJECT_CLASS_ERROR 5 -#define DUK_HOBJECT_CLASS_FUNCTION 6 -#define DUK_HOBJECT_CLASS_JSON 7 -#define DUK_HOBJECT_CLASS_MATH 8 -#define DUK_HOBJECT_CLASS_NUMBER 9 -#define DUK_HOBJECT_CLASS_OBJECT 10 +#define DUK_HOBJECT_CLASS_FUNCTION 3 +#define DUK_HOBJECT_CLASS_ARGUMENTS 4 +#define DUK_HOBJECT_CLASS_BOOLEAN 5 +#define DUK_HOBJECT_CLASS_DATE 6 +#define DUK_HOBJECT_CLASS_ERROR 7 +#define DUK_HOBJECT_CLASS_JSON 8 +#define DUK_HOBJECT_CLASS_MATH 9 +#define DUK_HOBJECT_CLASS_NUMBER 10 #define DUK_HOBJECT_CLASS_REGEXP 11 #define DUK_HOBJECT_CLASS_STRING 12 #define DUK_HOBJECT_CLASS_GLOBAL 13 -#define DUK_HOBJECT_CLASS_OBJENV 14 /* custom */ -#define DUK_HOBJECT_CLASS_DECENV 15 /* custom */ -#define DUK_HOBJECT_CLASS_BUFFER 16 /* custom; implies DUK_HOBJECT_IS_BUFFEROBJECT */ +#define DUK_HOBJECT_CLASS_SYMBOL 14 +#define DUK_HOBJECT_CLASS_OBJENV 15 /* custom */ +#define DUK_HOBJECT_CLASS_DECENV 16 /* custom */ #define DUK_HOBJECT_CLASS_POINTER 17 /* custom */ #define DUK_HOBJECT_CLASS_THREAD 18 /* custom; implies DUK_HOBJECT_IS_THREAD */ -#define DUK_HOBJECT_CLASS_ARRAYBUFFER 19 /* implies DUK_HOBJECT_IS_BUFFEROBJECT */ +#define DUK_HOBJECT_CLASS_BUFOBJ_MIN 19 +#define DUK_HOBJECT_CLASS_ARRAYBUFFER 19 /* implies DUK_HOBJECT_IS_BUFOBJ */ #define DUK_HOBJECT_CLASS_DATAVIEW 20 #define DUK_HOBJECT_CLASS_INT8ARRAY 21 #define DUK_HOBJECT_CLASS_UINT8ARRAY 22 @@ -4736,11 +6231,12 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_CLASS_UINT32ARRAY 27 #define DUK_HOBJECT_CLASS_FLOAT32ARRAY 28 #define DUK_HOBJECT_CLASS_FLOAT64ARRAY 29 +#define DUK_HOBJECT_CLASS_BUFOBJ_MAX 29 #define DUK_HOBJECT_CLASS_MAX 29 -/* class masks */ +/* Class masks. */ #define DUK_HOBJECT_CMASK_ALL ((1UL << (DUK_HOBJECT_CLASS_MAX + 1)) - 1UL) -#define DUK_HOBJECT_CMASK_UNUSED (1UL << DUK_HOBJECT_CLASS_UNUSED) +#define DUK_HOBJECT_CMASK_NONE (1UL << DUK_HOBJECT_CLASS_NONE) #define DUK_HOBJECT_CMASK_ARGUMENTS (1UL << DUK_HOBJECT_CLASS_ARGUMENTS) #define DUK_HOBJECT_CMASK_ARRAY (1UL << DUK_HOBJECT_CLASS_ARRAY) #define DUK_HOBJECT_CMASK_BOOLEAN (1UL << DUK_HOBJECT_CLASS_BOOLEAN) @@ -4754,11 +6250,10 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_CMASK_REGEXP (1UL << DUK_HOBJECT_CLASS_REGEXP) #define DUK_HOBJECT_CMASK_STRING (1UL << DUK_HOBJECT_CLASS_STRING) #define DUK_HOBJECT_CMASK_GLOBAL (1UL << DUK_HOBJECT_CLASS_GLOBAL) +#define DUK_HOBJECT_CMASK_SYMBOL (1UL << DUK_HOBJECT_CLASS_SYMBOL) #define DUK_HOBJECT_CMASK_OBJENV (1UL << DUK_HOBJECT_CLASS_OBJENV) #define DUK_HOBJECT_CMASK_DECENV (1UL << DUK_HOBJECT_CLASS_DECENV) -#define DUK_HOBJECT_CMASK_BUFFER (1UL << DUK_HOBJECT_CLASS_BUFFER) #define DUK_HOBJECT_CMASK_POINTER (1UL << DUK_HOBJECT_CLASS_POINTER) -#define DUK_HOBJECT_CMASK_THREAD (1UL << DUK_HOBJECT_CLASS_THREAD) #define DUK_HOBJECT_CMASK_ARRAYBUFFER (1UL << DUK_HOBJECT_CLASS_ARRAYBUFFER) #define DUK_HOBJECT_CMASK_DATAVIEW (1UL << DUK_HOBJECT_CLASS_DATAVIEW) #define DUK_HOBJECT_CMASK_INT8ARRAY (1UL << DUK_HOBJECT_CLASS_INT8ARRAY) @@ -4771,9 +6266,8 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_CMASK_FLOAT32ARRAY (1UL << DUK_HOBJECT_CLASS_FLOAT32ARRAY) #define DUK_HOBJECT_CMASK_FLOAT64ARRAY (1UL << DUK_HOBJECT_CLASS_FLOAT64ARRAY) -#define DUK_HOBJECT_CMASK_ALL_BUFFEROBJECTS \ - (DUK_HOBJECT_CMASK_BUFFER | \ - DUK_HOBJECT_CMASK_ARRAYBUFFER | \ +#define DUK_HOBJECT_CMASK_ALL_BUFOBJS \ + (DUK_HOBJECT_CMASK_ARRAYBUFFER | \ DUK_HOBJECT_CMASK_DATAVIEW | \ DUK_HOBJECT_CMASK_INT8ARRAY | \ DUK_HOBJECT_CMASK_UINT8ARRAY | \ @@ -4788,102 +6282,144 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_IS_OBJENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV) #define DUK_HOBJECT_IS_DECENV(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV) #define DUK_HOBJECT_IS_ENV(h) (DUK_HOBJECT_IS_OBJENV((h)) || DUK_HOBJECT_IS_DECENV((h))) -#define DUK_HOBJECT_IS_ARRAY(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_ARRAY) -#define DUK_HOBJECT_IS_COMPILEDFUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPILEDFUNCTION) -#define DUK_HOBJECT_IS_NATIVEFUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATIVEFUNCTION) -#define DUK_HOBJECT_IS_BUFFEROBJECT(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFFEROBJECT) -#define DUK_HOBJECT_IS_THREAD(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#define DUK_HOBJECT_IS_ARRAY(h) DUK_HOBJECT_HAS_EXOTIC_ARRAY((h)) /* Rely on class Array <=> exotic Array */ +#define DUK_HOBJECT_IS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) +#define DUK_HOBJECT_IS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) +#define DUK_HOBJECT_IS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +#define DUK_HOBJECT_IS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) +#else +#define DUK_HOBJECT_IS_BUFOBJ(h) 0 +#endif +#define DUK_HOBJECT_IS_THREAD(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_THREAD) +#if defined(DUK_USE_ES6_PROXY) +#define DUK_HOBJECT_IS_PROXY(h) DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((h)) +#else +#define DUK_HOBJECT_IS_PROXY(h) 0 +#endif #define DUK_HOBJECT_IS_NONBOUND_FUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \ - DUK_HOBJECT_FLAG_COMPILEDFUNCTION | \ - DUK_HOBJECT_FLAG_NATIVEFUNCTION) + DUK_HOBJECT_FLAG_COMPFUNC | \ + DUK_HOBJECT_FLAG_NATFUNC) #define DUK_HOBJECT_IS_FUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \ - DUK_HOBJECT_FLAG_BOUND | \ - DUK_HOBJECT_FLAG_COMPILEDFUNCTION | \ - DUK_HOBJECT_FLAG_NATIVEFUNCTION) + DUK_HOBJECT_FLAG_BOUNDFUNC | \ + DUK_HOBJECT_FLAG_COMPFUNC | \ + DUK_HOBJECT_FLAG_NATFUNC) -#define DUK_HOBJECT_IS_CALLABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \ - DUK_HOBJECT_FLAG_BOUND | \ - DUK_HOBJECT_FLAG_COMPILEDFUNCTION | \ - DUK_HOBJECT_FLAG_NATIVEFUNCTION) +#define DUK_HOBJECT_IS_CALLABLE(h) DUK_HOBJECT_HAS_CALLABLE((h)) -/* object has any exotic behavior(s) */ +/* Object has any exotic behavior(s). */ #define DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \ DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS | \ DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \ - DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | \ - DUK_HOBJECT_FLAG_BUFFEROBJECT | \ + DUK_HOBJECT_FLAG_BUFOBJ | \ DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) - #define DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS) +/* Object has any virtual properties (not counting Proxy behavior). */ +#define DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \ + DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \ + DUK_HOBJECT_FLAG_BUFOBJ) +#define DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS) + #define DUK_HOBJECT_HAS_EXTENSIBLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) #define DUK_HOBJECT_HAS_CONSTRUCTABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_HAS_BOUND(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND) -#define DUK_HOBJECT_HAS_COMPILEDFUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPILEDFUNCTION) -#define DUK_HOBJECT_HAS_NATIVEFUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATIVEFUNCTION) -#define DUK_HOBJECT_HAS_BUFFEROBJECT(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFFEROBJECT) -#define DUK_HOBJECT_HAS_THREAD(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#define DUK_HOBJECT_HAS_CALLABLE(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) +#define DUK_HOBJECT_HAS_BOUNDFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) +#define DUK_HOBJECT_HAS_COMPFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) +#define DUK_HOBJECT_HAS_NATFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +#define DUK_HOBJECT_HAS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) +#else +#define DUK_HOBJECT_HAS_BUFOBJ(h) 0 +#endif +#define DUK_HOBJECT_HAS_FASTREFS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) #define DUK_HOBJECT_HAS_ARRAY_PART(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) #define DUK_HOBJECT_HAS_STRICT(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) #define DUK_HOBJECT_HAS_NOTAIL(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_HAS_NEWENV(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_HAS_NAMEBINDING(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_HAS_CREATEARGS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_HAS_ENVRECCLOSED(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_HAS_HAVE_FINALIZER(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #define DUK_HOBJECT_HAS_EXOTIC_ARRAY(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) #define DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) #define DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#define DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC) +#if defined(DUK_USE_ES6_PROXY) #define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) +#else +#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h) 0 +#endif +#define DUK_HOBJECT_HAS_SPECIAL_CALL(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) #define DUK_HOBJECT_SET_EXTENSIBLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) #define DUK_HOBJECT_SET_CONSTRUCTABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_SET_BOUND(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND) -#define DUK_HOBJECT_SET_COMPILEDFUNCTION(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPILEDFUNCTION) -#define DUK_HOBJECT_SET_NATIVEFUNCTION(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATIVEFUNCTION) -#define DUK_HOBJECT_SET_BUFFEROBJECT(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFFEROBJECT) -#define DUK_HOBJECT_SET_THREAD(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#define DUK_HOBJECT_SET_CALLABLE(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) +#define DUK_HOBJECT_SET_BOUNDFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) +#define DUK_HOBJECT_SET_COMPFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) +#define DUK_HOBJECT_SET_NATFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +#define DUK_HOBJECT_SET_BUFOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) +#endif +#define DUK_HOBJECT_SET_FASTREFS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) #define DUK_HOBJECT_SET_ARRAY_PART(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) #define DUK_HOBJECT_SET_STRICT(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) #define DUK_HOBJECT_SET_NOTAIL(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_SET_NEWENV(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_SET_NAMEBINDING(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_SET_CREATEARGS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_SET_ENVRECCLOSED(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_SET_HAVE_FINALIZER(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #define DUK_HOBJECT_SET_EXOTIC_ARRAY(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) #define DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) #define DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#define DUK_HOBJECT_SET_EXOTIC_DUKFUNC(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC) +#if defined(DUK_USE_ES6_PROXY) #define DUK_HOBJECT_SET_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) +#endif +#define DUK_HOBJECT_SET_SPECIAL_CALL(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) #define DUK_HOBJECT_CLEAR_EXTENSIBLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE) #define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE) -#define DUK_HOBJECT_CLEAR_BOUND(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUND) -#define DUK_HOBJECT_CLEAR_COMPILEDFUNCTION(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPILEDFUNCTION) -#define DUK_HOBJECT_CLEAR_NATIVEFUNCTION(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATIVEFUNCTION) -#define DUK_HOBJECT_CLEAR_BUFFEROBJECT(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFFEROBJECT) -#define DUK_HOBJECT_CLEAR_THREAD(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#define DUK_HOBJECT_CLEAR_CALLABLE(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE) +#define DUK_HOBJECT_CLEAR_BOUNDFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC) +#define DUK_HOBJECT_CLEAR_COMPFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC) +#define DUK_HOBJECT_CLEAR_NATFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC) +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +#define DUK_HOBJECT_CLEAR_BUFOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) +#endif +#define DUK_HOBJECT_CLEAR_FASTREFS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS) #define DUK_HOBJECT_CLEAR_ARRAY_PART(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART) #define DUK_HOBJECT_CLEAR_STRICT(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT) #define DUK_HOBJECT_CLEAR_NOTAIL(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_CLEAR_NEWENV(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_CLEAR_NAMEBINDING(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_CLEAR_CREATEARGS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_CLEAR_ENVRECCLOSED(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #define DUK_HOBJECT_CLEAR_EXOTIC_ARRAY(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY) #define DUK_HOBJECT_CLEAR_EXOTIC_STRINGOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ) #define DUK_HOBJECT_CLEAR_EXOTIC_ARGUMENTS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS) -#define DUK_HOBJECT_CLEAR_EXOTIC_DUKFUNC(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC) +#if defined(DUK_USE_ES6_PROXY) #define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) +#endif +#define DUK_HOBJECT_CLEAR_SPECIAL_CALL(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL) -/* flags used for property attributes in duk_propdesc and packed flags */ -#define DUK_PROPDESC_FLAG_WRITABLE (1 << 0) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_ENUMERABLE (1 << 1) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_CONFIGURABLE (1 << 2) /* E5 Section 8.6.1 */ -#define DUK_PROPDESC_FLAG_ACCESSOR (1 << 3) /* accessor */ -#define DUK_PROPDESC_FLAG_VIRTUAL (1 << 4) /* property is virtual: used in duk_propdesc, never stored +/* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond + * duk_hobject base header. This is used just for asserts so doesn't need to + * be optimized. + */ +#define DUK_HOBJECT_PROHIBITS_FASTREFS(h) \ + (DUK_HOBJECT_IS_COMPFUNC((h)) || DUK_HOBJECT_IS_DECENV((h)) || DUK_HOBJECT_IS_OBJENV((h)) || \ + DUK_HOBJECT_IS_BUFOBJ((h)) || DUK_HOBJECT_IS_THREAD((h)) || DUK_HOBJECT_IS_PROXY((h)) || \ + DUK_HOBJECT_IS_BOUNDFUNC((h))) +#define DUK_HOBJECT_ALLOWS_FASTREFS(h) (!DUK_HOBJECT_PROHIBITS_FASTREFS((h))) + +/* Flags used for property attributes in duk_propdesc and packed flags. + * Must fit into 8 bits. + */ +#define DUK_PROPDESC_FLAG_WRITABLE (1U << 0) /* E5 Section 8.6.1 */ +#define DUK_PROPDESC_FLAG_ENUMERABLE (1U << 1) /* E5 Section 8.6.1 */ +#define DUK_PROPDESC_FLAG_CONFIGURABLE (1U << 2) /* E5 Section 8.6.1 */ +#define DUK_PROPDESC_FLAG_ACCESSOR (1U << 3) /* accessor */ +#define DUK_PROPDESC_FLAG_VIRTUAL (1U << 4) /* property is virtual: used in duk_propdesc, never stored * (used by e.g. buffer virtual properties) */ #define DUK_PROPDESC_FLAGS_MASK (DUK_PROPDESC_FLAG_WRITABLE | \ @@ -4891,12 +6427,12 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); DUK_PROPDESC_FLAG_CONFIGURABLE | \ DUK_PROPDESC_FLAG_ACCESSOR) -/* additional flags which are passed in the same flags argument as property +/* Additional flags which are passed in the same flags argument as property * flags but are not stored in object properties. */ -#define DUK_PROPDESC_FLAG_NO_OVERWRITE (1 << 4) /* internal define property: skip write silently if exists */ +#define DUK_PROPDESC_FLAG_NO_OVERWRITE (1U << 4) /* internal define property: skip write silently if exists */ -/* convenience */ +/* Convenience defines for property attributes. */ #define DUK_PROPDESC_FLAGS_NONE 0 #define DUK_PROPDESC_FLAGS_W (DUK_PROPDESC_FLAG_WRITABLE) #define DUK_PROPDESC_FLAGS_E (DUK_PROPDESC_FLAG_ENUMERABLE) @@ -4908,9 +6444,9 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); DUK_PROPDESC_FLAG_ENUMERABLE | \ DUK_PROPDESC_FLAG_CONFIGURABLE) -/* flags for duk_hobject_get_own_propdesc() and variants */ -#define DUK_GETDESC_FLAG_PUSH_VALUE (1 << 0) /* push value to stack */ -#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP (1 << 1) /* don't throw for prototype loop */ +/* Flags for duk_hobject_get_own_propdesc() and variants. */ +#define DUK_GETDESC_FLAG_PUSH_VALUE (1U << 0) /* push value to stack */ +#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP (1U << 1) /* don't throw for prototype loop */ /* * Macro for object validity check @@ -4918,24 +6454,12 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); * Assert for currently guaranteed relations between flags, for instance. */ -#define DUK_ASSERT_HOBJECT_VALID(h) do { \ - DUK_ASSERT((h) != NULL); \ - DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE((h)) || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FUNCTION); \ - DUK_ASSERT(!DUK_HOBJECT_IS_BUFFEROBJECT((h)) || \ - (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_BUFFER || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_ARRAYBUFFER || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DATAVIEW || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT8ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT8ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT16ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT16ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT32ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT32ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT32ARRAY || \ - DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT64ARRAY)); \ - } while (0) +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hobject_assert_valid(duk_hobject *h); +#define DUK_HOBJECT_ASSERT_VALID(h) do { duk_hobject_assert_valid((h)); } while (0) +#else +#define DUK_HOBJECT_ASSERT_VALID(h) do {} while (0) +#endif /* * Macros to access the 'props' allocation. @@ -5217,11 +6741,8 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); */ #define DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY 10000L -/* Maximum traversal depth for "bound function" chains. */ -#define DUK_HOBJECT_BOUND_CHAIN_SANITY 10000L - /* - * Ecmascript [[Class]] + * ECMAScript [[Class]] */ /* range check not necessary because all 4-bit values are mapped */ @@ -5251,9 +6772,32 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); } while (0) #endif -/* note: this updates refcounts */ +/* Set prototype, DECREF earlier value, INCREF new value (tolerating NULLs). */ #define DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr,h,p) duk_hobject_set_prototype_updref((thr), (h), (p)) +/* Set initial prototype, assume NULL previous prototype, INCREF new value, + * tolerate NULL. + */ +#define DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr,h,proto) do { \ + duk_hthread *duk__thr = (thr); \ + duk_hobject *duk__obj = (h); \ + duk_hobject *duk__proto = (proto); \ + DUK_UNREF(duk__thr); \ + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(duk__thr->heap, duk__obj) == NULL); \ + DUK_HOBJECT_SET_PROTOTYPE(duk__thr->heap, duk__obj, duk__proto); \ + DUK_HOBJECT_INCREF_ALLOWNULL(duk__thr, duk__proto); \ + } while (0) + +/* + * Finalizer check + */ + +#if defined(DUK_USE_HEAPPTR16) +#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((heap), (h)) +#else +#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((h)) +#endif + /* * Resizing and hash behavior */ @@ -5267,22 +6811,9 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #if defined(DUK_USE_OBJSIZES16) #define DUK_HOBJECT_MAX_PROPERTIES 0x0000ffffUL #else -#define DUK_HOBJECT_MAX_PROPERTIES 0x7fffffffUL /* 2**31-1 ~= 2G properties */ +#define DUK_HOBJECT_MAX_PROPERTIES 0x3fffffffUL /* 2**30-1 ~= 1G properties */ #endif -/* higher value conserves memory; also note that linear scan is cache friendly */ -#define DUK_HOBJECT_E_USE_HASH_LIMIT 32 - -/* hash size relative to entries size: for value X, approx. hash_prime(e_size + e_size / X) */ -#define DUK_HOBJECT_H_SIZE_DIVISOR 4 /* hash size approx. 1.25 times entries size */ - -/* if new_size < L * old_size, resize without abandon check; L = 3-bit fixed point, e.g. 9 -> 9/8 = 112.5% */ -#define DUK_HOBJECT_A_FAST_RESIZE_LIMIT 9 /* 112.5%, i.e. new size less than 12.5% higher -> fast resize */ - -/* if density < L, abandon array part, L = 3-bit fixed point, e.g. 2 -> 2/8 = 25% */ -/* limit is quite low: one array entry is 8 bytes, one normal entry is 4+1+8+4 = 17 bytes (with hash entry) */ -#define DUK_HOBJECT_A_ABANDON_LIMIT 2 /* 25%, i.e. less than 25% used -> abandon */ - /* internal align target for props allocation, must be 2*n for some n */ #if (DUK_USE_ALIGN_BY == 4) #define DUK_HOBJECT_ALIGN_TARGET 4 @@ -5294,18 +6825,6 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #error invalid DUK_USE_ALIGN_BY #endif -/* controls for minimum entry part growth */ -#define DUK_HOBJECT_E_MIN_GROW_ADD 16 -#define DUK_HOBJECT_E_MIN_GROW_DIVISOR 8 /* 2^3 -> 1/8 = 12.5% min growth */ - -/* controls for minimum array part growth */ -#define DUK_HOBJECT_A_MIN_GROW_ADD 16 -#define DUK_HOBJECT_A_MIN_GROW_DIVISOR 8 /* 2^3 -> 1/8 = 12.5% min growth */ - -/* probe sequence */ -#define DUK_HOBJECT_HASH_INITIAL(hash,h_size) ((hash) % (h_size)) -#define DUK_HOBJECT_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash)) - /* * PC-to-line constants */ @@ -5335,7 +6854,7 @@ union duk_propvalue { struct duk_propdesc { /* read-only values 'lifted' for ease of use */ - duk_small_int_t flags; + duk_small_uint_t flags; duk_hobject *get; duk_hobject *set; @@ -5416,7 +6935,7 @@ struct duk_hobject { #if defined(DUK_USE_HEAPPTR16) /* Located in duk_heaphdr h_extra16. Subclasses of duk_hobject (like - * duk_hcompiledfunction) are not free to use h_extra16 for this reason. + * duk_hcompfunc) are not free to use h_extra16 for this reason. */ #else duk_uint8_t *props; @@ -5459,28 +6978,45 @@ DUK_INTERNAL_DECL duk_uint8_t duk_class_number_to_stridx[32]; */ /* alloc and init */ -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobject_flags); -#if 0 /* unused */ -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags); +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +#endif +DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags); + +/* resize */ +DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr, + duk_hobject *obj, + duk_uint32_t new_e_size, + duk_uint32_t new_a_size, + duk_uint32_t new_h_size, + duk_bool_t abandon_array); +DUK_INTERNAL_DECL void duk_hobject_resize_entrypart(duk_hthread *thr, + duk_hobject *obj, + duk_uint32_t new_e_size); +#if 0 /*unused*/ +DUK_INTERNAL_DECL void duk_hobject_resize_arraypart(duk_hthread *thr, + duk_hobject *obj, + duk_uint32_t new_a_size); #endif -DUK_INTERNAL_DECL duk_hcompiledfunction *duk_hcompiledfunction_alloc(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hnativefunction *duk_hnativefunction_alloc(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL duk_hbufferobject *duk_hbufferobject_alloc(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_flags); /* low-level property functions */ -DUK_INTERNAL_DECL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *out_attrs); -DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i); +DUK_INTERNAL_DECL duk_bool_t duk_hobject_find_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx); +DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key); +DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_stridx(duk_heap *heap, duk_hobject *obj, duk_small_uint_t stridx); +DUK_INTERNAL_DECL duk_tval *duk_hobject_find_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint_t *out_attrs); +DUK_INTERNAL_DECL duk_tval *duk_hobject_find_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i); DUK_INTERNAL_DECL duk_bool_t duk_hobject_get_own_propdesc(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags); -/* XXX: when optimizing for guaranteed property slots, use a guaranteed - * slot for internal value; this call can then access it directly. - */ -#define duk_hobject_get_internal_value_tval_ptr(heap,obj) \ - duk_hobject_find_existing_entry_tval_ptr((heap), (obj), DUK_HEAP_STRING_INT_VALUE((heap))) - /* core property functions */ DUK_INTERNAL_DECL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key); DUK_INTERNAL_DECL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag); @@ -5488,84 +7024,92 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_ DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key); /* internal property functions */ -#define DUK_DELPROP_FLAG_THROW (1 << 0) -#define DUK_DELPROP_FLAG_FORCE (1 << 1) +#define DUK_DELPROP_FLAG_THROW (1U << 0) +#define DUK_DELPROP_FLAG_FORCE (1U << 1) DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key); DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags); DUK_INTERNAL_DECL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_hobject_define_accessor_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_hobject *getter, duk_hobject *setter, duk_small_uint_t propflags); -DUK_INTERNAL_DECL void duk_hobject_set_length(duk_hthread *thr, duk_hobject *obj, duk_uint32_t length); /* XXX: duk_uarridx_t? */ -DUK_INTERNAL_DECL void duk_hobject_set_length_zero(duk_hthread *thr, duk_hobject *obj); -DUK_INTERNAL_DECL duk_uint32_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); /* XXX: duk_uarridx_t? */ +DUK_INTERNAL_DECL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); +#if defined(DUK_USE_HEAPPTR16) +DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj); +#else +DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj); +#endif /* helpers for defineProperty() and defineProperties() */ -DUK_INTERNAL_DECL -void duk_hobject_prepare_property_descriptor(duk_context *ctx, - duk_idx_t idx_in, - duk_uint_t *out_defprop_flags, - duk_idx_t *out_idx_value, - duk_hobject **out_getter, - duk_hobject **out_setter); -DUK_INTERNAL_DECL -void duk_hobject_define_property_helper(duk_context *ctx, - duk_uint_t defprop_flags, - duk_hobject *obj, - duk_hstring *key, - duk_idx_t idx_value, - duk_hobject *get, - duk_hobject *set); +DUK_INTERNAL_DECL void duk_hobject_prepare_property_descriptor(duk_hthread *thr, + duk_idx_t idx_in, + duk_uint_t *out_defprop_flags, + duk_idx_t *out_idx_value, + duk_hobject **out_getter, + duk_hobject **out_setter); +DUK_INTERNAL_DECL duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr, + duk_uint_t defprop_flags, + duk_hobject *obj, + duk_hstring *key, + duk_idx_t idx_value, + duk_hobject *get, + duk_hobject *set, + duk_bool_t throw_flag); /* Object built-in methods */ -DUK_INTERNAL_DECL duk_ret_t duk_hobject_object_get_own_property_descriptor(duk_context *ctx); +DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx); DUK_INTERNAL_DECL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze); DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_context *ctx, duk_small_uint_t required_desc_flags); +DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags); /* internal properties */ -DUK_INTERNAL_DECL duk_bool_t duk_hobject_get_internal_value(duk_heap *heap, duk_hobject *obj, duk_tval *tv); +DUK_INTERNAL_DECL duk_tval *duk_hobject_get_internal_value_tval_ptr(duk_heap *heap, duk_hobject *obj); DUK_INTERNAL_DECL duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *obj); +DUK_INTERNAL_DECL duk_harray *duk_hobject_get_formals(duk_hthread *thr, duk_hobject *obj); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_get_varmap(duk_hthread *thr, duk_hobject *obj); /* hobject management functions */ DUK_INTERNAL_DECL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj); -/* ES6 proxy */ +/* ES2015 proxy */ #if defined(DUK_USE_ES6_PROXY) -DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hthread *thr, duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler); -DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hthread *thr, duk_hobject *obj); +DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj); #endif /* enumeration */ -DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint_t enum_flags); -DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_small_uint_t enum_flags); -DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value); +DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags); +DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags); +DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value); /* macros */ DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p); -/* finalization */ -DUK_INTERNAL_DECL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj); - /* pc2line */ #if defined(DUK_USE_PC2LINE) DUK_INTERNAL_DECL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length); -DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_idx_t idx_func, duk_uint_fast32_t pc); +DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc); #endif /* misc */ DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *thr, duk_hobject *h, duk_hobject *p, duk_bool_t ignore_loop); +#if !defined(DUK_USE_OBJECT_BUILTIN) +/* These declarations are needed when related built-in is disabled and + * genbuiltins.py won't automatically emit the declerations. + */ +DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr); +DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_hthread *thr); +#endif + #endif /* DUK_HOBJECT_H_INCLUDED */ -#line 1 "duk_hcompiledfunction.h" +/* #include duk_hcompfunc.h */ /* - * Heap compiled function (Ecmascript function) representation. + * Heap compiled function (ECMAScript function) representation. * - * There is a single data buffer containing the Ecmascript function's + * There is a single data buffer containing the ECMAScript function's * bytecode, constants, and inner functions. */ -#ifndef DUK_HCOMPILEDFUNCTION_H_INCLUDED -#define DUK_HCOMPILEDFUNCTION_H_INCLUDED +#if !defined(DUK_HCOMPFUNC_H_INCLUDED) +#define DUK_HCOMPFUNC_H_INCLUDED /* * Field accessor macros @@ -5574,37 +7118,52 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *t /* XXX: casts could be improved, especially for GET/SET DATA */ #if defined(DUK_USE_HEAPPTR16) -#define DUK_HCOMPILEDFUNCTION_GET_DATA(heap,h) \ +#define DUK_HCOMPFUNC_GET_DATA(heap,h) \ ((duk_hbuffer_fixed *) (void *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->data16)) -#define DUK_HCOMPILEDFUNCTION_SET_DATA(heap,h,v) do { \ +#define DUK_HCOMPFUNC_SET_DATA(heap,h,v) do { \ (h)->data16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ } while (0) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS(heap,h) \ +#define DUK_HCOMPFUNC_GET_FUNCS(heap,h) \ ((duk_hobject **) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->funcs16))) -#define DUK_HCOMPILEDFUNCTION_SET_FUNCS(heap,h,v) do { \ +#define DUK_HCOMPFUNC_SET_FUNCS(heap,h,v) do { \ (h)->funcs16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ } while (0) -#define DUK_HCOMPILEDFUNCTION_GET_BYTECODE(heap,h) \ +#define DUK_HCOMPFUNC_GET_BYTECODE(heap,h) \ ((duk_instr_t *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->bytecode16))) -#define DUK_HCOMPILEDFUNCTION_SET_BYTECODE(heap,h,v) do { \ +#define DUK_HCOMPFUNC_SET_BYTECODE(heap,h,v) do { \ (h)->bytecode16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ } while (0) +#define DUK_HCOMPFUNC_GET_LEXENV(heap,h) \ + ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->lex_env16))) +#define DUK_HCOMPFUNC_SET_LEXENV(heap,h,v) do { \ + (h)->lex_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ + } while (0) +#define DUK_HCOMPFUNC_GET_VARENV(heap,h) \ + ((duk_hobject *) (void *) (DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (h)->var_env16))) +#define DUK_HCOMPFUNC_SET_VARENV(heap,h,v) do { \ + (h)->var_env16 = DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (void *) (v)); \ + } while (0) #else -#define DUK_HCOMPILEDFUNCTION_GET_DATA(heap,h) \ - ((duk_hbuffer_fixed *) (void *) (h)->data) -#define DUK_HCOMPILEDFUNCTION_SET_DATA(heap,h,v) do { \ +#define DUK_HCOMPFUNC_GET_DATA(heap,h) ((duk_hbuffer_fixed *) (void *) (h)->data) +#define DUK_HCOMPFUNC_SET_DATA(heap,h,v) do { \ (h)->data = (duk_hbuffer *) (v); \ } while (0) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS(heap,h) \ - ((h)->funcs) -#define DUK_HCOMPILEDFUNCTION_SET_FUNCS(heap,h,v) do { \ +#define DUK_HCOMPFUNC_GET_FUNCS(heap,h) ((h)->funcs) +#define DUK_HCOMPFUNC_SET_FUNCS(heap,h,v) do { \ (h)->funcs = (v); \ } while (0) -#define DUK_HCOMPILEDFUNCTION_GET_BYTECODE(heap,h) \ - ((h)->bytecode) -#define DUK_HCOMPILEDFUNCTION_SET_BYTECODE(heap,h,v) do { \ +#define DUK_HCOMPFUNC_GET_BYTECODE(heap,h) ((h)->bytecode) +#define DUK_HCOMPFUNC_SET_BYTECODE(heap,h,v) do { \ (h)->bytecode = (v); \ } while (0) +#define DUK_HCOMPFUNC_GET_LEXENV(heap,h) ((h)->lex_env) +#define DUK_HCOMPFUNC_SET_LEXENV(heap,h,v) do { \ + (h)->lex_env = (v); \ + } while (0) +#define DUK_HCOMPFUNC_GET_VARENV(heap,h) ((h)->var_env) +#define DUK_HCOMPFUNC_SET_VARENV(heap,h,v) do { \ + (h)->var_env = (v); \ + } while (0) #endif /* @@ -5612,71 +7171,81 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *t */ /* Note: assumes 'data' is always a fixed buffer */ -#define DUK_HCOMPILEDFUNCTION_GET_BUFFER_BASE(heap,h) \ - DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPILEDFUNCTION_GET_DATA((heap), (h))) +#define DUK_HCOMPFUNC_GET_BUFFER_BASE(heap,h) \ + DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h))) -#define DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(heap,h) \ - ((duk_tval *) (void *) DUK_HCOMPILEDFUNCTION_GET_BUFFER_BASE((heap), (h))) +#define DUK_HCOMPFUNC_GET_CONSTS_BASE(heap,h) \ + ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_BUFFER_BASE((heap), (h))) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(heap,h) \ - DUK_HCOMPILEDFUNCTION_GET_FUNCS((heap), (h)) +#define DUK_HCOMPFUNC_GET_FUNCS_BASE(heap,h) \ + DUK_HCOMPFUNC_GET_FUNCS((heap), (h)) -#define DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(heap,h) \ - DUK_HCOMPILEDFUNCTION_GET_BYTECODE((heap), (h)) +#define DUK_HCOMPFUNC_GET_CODE_BASE(heap,h) \ + DUK_HCOMPFUNC_GET_BYTECODE((heap), (h)) -#define DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(heap,h) \ - ((duk_tval *) (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS((heap), (h))) +#define DUK_HCOMPFUNC_GET_CONSTS_END(heap,h) \ + ((duk_tval *) (void *) DUK_HCOMPFUNC_GET_FUNCS((heap), (h))) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(heap,h) \ - ((duk_hobject **) (void *) DUK_HCOMPILEDFUNCTION_GET_BYTECODE((heap), (h))) +#define DUK_HCOMPFUNC_GET_FUNCS_END(heap,h) \ + ((duk_hobject **) (void *) DUK_HCOMPFUNC_GET_BYTECODE((heap), (h))) -/* XXX: double evaluation of DUK_HCOMPILEDFUNCTION_GET_DATA() */ -#define DUK_HCOMPILEDFUNCTION_GET_CODE_END(heap,h) \ - ((duk_instr_t *) (void *) (DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPILEDFUNCTION_GET_DATA((heap), (h))) + \ - DUK_HBUFFER_GET_SIZE((duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA((heap), h)))) +/* XXX: double evaluation of DUK_HCOMPFUNC_GET_DATA() */ +#define DUK_HCOMPFUNC_GET_CODE_END(heap,h) \ + ((duk_instr_t *) (void *) (DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), DUK_HCOMPFUNC_GET_DATA((heap), (h))) + \ + DUK_HBUFFER_GET_SIZE((duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA((heap), h)))) -#define DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(heap,h) \ +#define DUK_HCOMPFUNC_GET_CONSTS_SIZE(heap,h) \ ( \ (duk_size_t) \ ( \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END((heap), (h))) - \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE((heap), (h))) \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_END((heap), (h))) - \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CONSTS_BASE((heap), (h))) \ ) \ ) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(heap,h) \ +#define DUK_HCOMPFUNC_GET_FUNCS_SIZE(heap,h) \ ( \ (duk_size_t) \ ( \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END((heap), (h))) - \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE((heap), (h))) \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_END((heap), (h))) - \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_FUNCS_BASE((heap), (h))) \ ) \ ) -#define DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(heap,h) \ +#define DUK_HCOMPFUNC_GET_CODE_SIZE(heap,h) \ ( \ (duk_size_t) \ ( \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_END((heap),(h))) - \ - ((const duk_uint8_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE((heap),(h))) \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_END((heap),(h))) - \ + ((const duk_uint8_t *) DUK_HCOMPFUNC_GET_CODE_BASE((heap),(h))) \ ) \ ) -#define DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(heap,h) \ - ((duk_size_t) (DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE((heap), (h)) / sizeof(duk_tval))) +#define DUK_HCOMPFUNC_GET_CONSTS_COUNT(heap,h) \ + ((duk_size_t) (DUK_HCOMPFUNC_GET_CONSTS_SIZE((heap), (h)) / sizeof(duk_tval))) -#define DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(heap,h) \ - ((duk_size_t) (DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE((heap), (h)) / sizeof(duk_hobject *))) +#define DUK_HCOMPFUNC_GET_FUNCS_COUNT(heap,h) \ + ((duk_size_t) (DUK_HCOMPFUNC_GET_FUNCS_SIZE((heap), (h)) / sizeof(duk_hobject *))) -#define DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(heap,h) \ - ((duk_size_t) (DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE((heap), (h)) / sizeof(duk_instr_t))) +#define DUK_HCOMPFUNC_GET_CODE_COUNT(heap,h) \ + ((duk_size_t) (DUK_HCOMPFUNC_GET_CODE_SIZE((heap), (h)) / sizeof(duk_instr_t))) +/* + * Validity assert + */ + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hcompfunc_assert_valid(duk_hcompfunc *h); +#define DUK_HCOMPFUNC_ASSERT_VALID(h) do { duk_hcompfunc_assert_valid((h)); } while (0) +#else +#define DUK_HCOMPFUNC_ASSERT_VALID(h) do {} while (0) +#endif /* * Main struct */ -struct duk_hcompiledfunction { +struct duk_hcompfunc { /* shared object part */ duk_hobject obj; @@ -5723,6 +7292,17 @@ struct duk_hcompiledfunction { duk_instr_t *bytecode; #endif + /* Lexenv: lexical environment of closure, NULL for templates. + * Varenv: variable environment of closure, NULL for templates. + */ +#if defined(DUK_USE_HEAPPTR16) + duk_uint16_t lex_env16; + duk_uint16_t var_env16; +#else + duk_hobject *lex_env; + duk_hobject *var_env; +#endif + /* * 'nregs' registers are allocated on function entry, at most 'nargs' * are initialized to arguments, and the rest to undefined. Arguments @@ -5778,8 +7358,6 @@ struct duk_hcompiledfunction { * _Formals: [ "arg1", "arg2" ], * _Source: "function func(arg1, arg2) { ... }", * _Pc2line: , - * _Varenv: , - * _Lexenv: * } * * More detailed description of these properties can be found @@ -5795,19 +7373,26 @@ struct duk_hcompiledfunction { #endif }; -#endif /* DUK_HCOMPILEDFUNCTION_H_INCLUDED */ -#line 1 "duk_hnativefunction.h" +#endif /* DUK_HCOMPFUNC_H_INCLUDED */ +/* #include duk_hnatfunc.h */ /* * Heap native function representation. */ -#ifndef DUK_HNATIVEFUNCTION_H_INCLUDED -#define DUK_HNATIVEFUNCTION_H_INCLUDED +#if !defined(DUK_HNATFUNC_H_INCLUDED) +#define DUK_HNATFUNC_H_INCLUDED -#define DUK_HNATIVEFUNCTION_NARGS_VARARGS ((duk_int16_t) -1) -#define DUK_HNATIVEFUNCTION_NARGS_MAX ((duk_int16_t) 0x7fff) +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hnatfunc_assert_valid(duk_hnatfunc *h); +#define DUK_HNATFUNC_ASSERT_VALID(h) do { duk_hnatfunc_assert_valid((h)); } while (0) +#else +#define DUK_HNATFUNC_ASSERT_VALID(h) do {} while (0) +#endif -struct duk_hnativefunction { +#define DUK_HNATFUNC_NARGS_VARARGS ((duk_int16_t) -1) +#define DUK_HNATFUNC_NARGS_MAX ((duk_int16_t) 0x7fff) + +struct duk_hnatfunc { /* shared object part */ duk_hobject obj; @@ -5824,113 +7409,140 @@ struct duk_hnativefunction { * versa. * * Note: cannot place nargs/magic into the heaphdr flags, because - * duk_hobject takes almost all flags already (and needs the spare). + * duk_hobject takes almost all flags already. */ }; -#endif /* DUK_HNATIVEFUNCTION_H_INCLUDED */ -#line 1 "duk_hbufferobject.h" +#endif /* DUK_HNATFUNC_H_INCLUDED */ +/* #include duk_hboundfunc.h */ +/* + * Bound function representation. + */ + +#if !defined(DUK_HBOUNDFUNC_H_INCLUDED) +#define DUK_HBOUNDFUNC_H_INCLUDED + +/* Artificial limit for args length. Ensures arithmetic won't overflow + * 32 bits when combining bound functions. + */ +#define DUK_HBOUNDFUNC_MAX_ARGS 0x20000000UL + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hboundfunc_assert_valid(duk_hboundfunc *h); +#define DUK_HBOUNDFUNC_ASSERT_VALID(h) do { duk_hboundfunc_assert_valid((h)); } while (0) +#else +#define DUK_HBOUNDFUNC_ASSERT_VALID(h) do {} while (0) +#endif + +struct duk_hboundfunc { + /* Shared object part. */ + duk_hobject obj; + + /* Final target function, stored as duk_tval so that lightfunc can be + * represented too. + */ + duk_tval target; + + /* This binding. */ + duk_tval this_binding; + + /* Arguments to prepend. */ + duk_tval *args; /* Separate allocation. */ + duk_idx_t nargs; +}; + +#endif /* DUK_HBOUNDFUNC_H_INCLUDED */ +/* #include duk_hbufobj.h */ /* * Heap Buffer object representation. Used for all Buffer variants. */ -#ifndef DUK_HBUFFEROBJECT_H_INCLUDED -#define DUK_HBUFFEROBJECT_H_INCLUDED +#if !defined(DUK_HBUFOBJ_H_INCLUDED) +#define DUK_HBUFOBJ_H_INCLUDED + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* All element accessors are host endian now (driven by TypedArray spec). */ -#define DUK_HBUFFEROBJECT_ELEM_UINT8 0 -#define DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED 1 -#define DUK_HBUFFEROBJECT_ELEM_INT8 2 -#define DUK_HBUFFEROBJECT_ELEM_UINT16 3 -#define DUK_HBUFFEROBJECT_ELEM_INT16 4 -#define DUK_HBUFFEROBJECT_ELEM_UINT32 5 -#define DUK_HBUFFEROBJECT_ELEM_INT32 6 -#define DUK_HBUFFEROBJECT_ELEM_FLOAT32 7 -#define DUK_HBUFFEROBJECT_ELEM_FLOAT64 8 -#define DUK_HBUFFEROBJECT_ELEM_MAX 8 +#define DUK_HBUFOBJ_ELEM_UINT8 0 +#define DUK_HBUFOBJ_ELEM_UINT8CLAMPED 1 +#define DUK_HBUFOBJ_ELEM_INT8 2 +#define DUK_HBUFOBJ_ELEM_UINT16 3 +#define DUK_HBUFOBJ_ELEM_INT16 4 +#define DUK_HBUFOBJ_ELEM_UINT32 5 +#define DUK_HBUFOBJ_ELEM_INT32 6 +#define DUK_HBUFOBJ_ELEM_FLOAT32 7 +#define DUK_HBUFOBJ_ELEM_FLOAT64 8 +#define DUK_HBUFOBJ_ELEM_MAX 8 -#define DUK_ASSERT_HBUFFEROBJECT_VALID(h) do { \ - DUK_ASSERT((h) != NULL); \ - DUK_ASSERT((h)->shift <= 3); \ - DUK_ASSERT((h)->elem_type <= DUK_HBUFFEROBJECT_ELEM_MAX); \ - DUK_ASSERT(((h)->shift == 0 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8) || \ - ((h)->shift == 0 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED) || \ - ((h)->shift == 0 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_INT8) || \ - ((h)->shift == 1 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT16) || \ - ((h)->shift == 1 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_INT16) || \ - ((h)->shift == 2 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT32) || \ - ((h)->shift == 2 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_INT32) || \ - ((h)->shift == 2 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_FLOAT32) || \ - ((h)->shift == 3 && (h)->elem_type == DUK_HBUFFEROBJECT_ELEM_FLOAT64)); \ - DUK_ASSERT((h)->is_view == 0 || (h)->is_view == 1); \ - DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) (h))); \ - if ((h)->buf == NULL) { \ - DUK_ASSERT((h)->offset == 0); \ - DUK_ASSERT((h)->length == 0); \ - } else { \ - /* No assertions for offset or length; in particular, \ - * it's OK for length to be longer than underlying \ - * buffer. Just ensure they don't wrap when added. \ - */ \ - DUK_ASSERT((h)->offset + (h)->length >= (h)->offset); \ - } \ - } while (0) +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hbufobj_assert_valid(duk_hbufobj *h); +#define DUK_HBUFOBJ_ASSERT_VALID(h) do { duk_hbufobj_assert_valid((h)); } while (0) +#else +#define DUK_HBUFOBJ_ASSERT_VALID(h) do {} while (0) +#endif /* Get the current data pointer (caller must ensure buf != NULL) as a - * duk_uint8_t ptr. + * duk_uint8_t ptr. Note that the result may be NULL if the underlying + * buffer has zero size and is not a fixed buffer. */ -#define DUK_HBUFFEROBJECT_GET_SLICE_BASE(heap,h) \ +#define DUK_HBUFOBJ_GET_SLICE_BASE(heap,h) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ (((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR((heap), (h)->buf)) + (h)->offset)) /* True if slice is full, i.e. offset is zero and length covers the entire - * buffer. This status may change independently of the duk_hbufferobject if - * the underlying buffer is dynamic and changes without the hbufferobject + * buffer. This status may change independently of the duk_hbufobj if + * the underlying buffer is dynamic and changes without the hbufobj * being changed. */ -#define DUK_HBUFFEROBJECT_FULL_SLICE(h) \ +#define DUK_HBUFOBJ_FULL_SLICE(h) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ ((h)->offset == 0 && (h)->length == DUK_HBUFFER_GET_SIZE((h)->buf))) /* Validate that the whole slice [0,length[ is contained in the underlying * buffer. Caller must ensure 'buf' != NULL. */ -#define DUK_HBUFFEROBJECT_VALID_SLICE(h) \ +#define DUK_HBUFOBJ_VALID_SLICE(h) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ ((h)->offset + (h)->length <= DUK_HBUFFER_GET_SIZE((h)->buf))) /* Validate byte read/write for virtual 'offset', i.e. check that the * offset, taking into account h->offset, is within the underlying * buffer size. This is a safety check which is needed to ensure - * that even a misconfigured duk_hbufferobject never causes memory - * unsafe behavior (e.g. if an underlying dynamic buffer changes - * after being setup). Caller must ensure 'buf' != NULL. + * that even a misconfigured duk_hbufobj never causes memory unsafe + * behavior (e.g. if an underlying dynamic buffer changes after being + * setup). Caller must ensure 'buf' != NULL. */ -#define DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_INCL(h,off) \ +#define DUK_HBUFOBJ_VALID_BYTEOFFSET_INCL(h,off) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ ((h)->offset + (off) < DUK_HBUFFER_GET_SIZE((h)->buf))) -#define DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h,off) \ +#define DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h,off) \ (DUK_ASSERT_EXPR((h) != NULL), DUK_ASSERT_EXPR((h)->buf != NULL), \ ((h)->offset + (off) <= DUK_HBUFFER_GET_SIZE((h)->buf))) /* Clamp an input byte length (already assumed to be within the nominal - * duk_hbufferobject 'length') to the current dynamic buffer limits to - * yield a byte length limit that's safe for memory accesses. This value - * can be invalidated by any side effect because it may trigger a user + * duk_hbufobj 'length') to the current dynamic buffer limits to yield + * a byte length limit that's safe for memory accesses. This value can + * be invalidated by any side effect because it may trigger a user * callback that resizes the underlying buffer. */ -#define DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h,len) \ +#define DUK_HBUFOBJ_CLAMP_BYTELENGTH(h,len) \ (DUK_ASSERT_EXPR((h) != NULL), \ - duk_hbufferobject_clamp_bytelength((h), (len))) + duk_hbufobj_clamp_bytelength((h), (len))) -struct duk_hbufferobject { +/* Typed arrays have virtual indices, ArrayBuffer and DataView do not. */ +#define DUK_HBUFOBJ_HAS_VIRTUAL_INDICES(h) ((h)->is_typedarray) + +struct duk_hbufobj { /* Shared object part. */ duk_hobject obj; /* Underlying buffer (refcounted), may be NULL. */ duk_hbuffer *buf; + /* .buffer reference to an ArrayBuffer, may be NULL. */ + duk_hobject *buf_prop; + /* Slice and accessor information. * * Because the underlying buffer may be dynamic, these may be @@ -5953,86 +7565,77 @@ struct duk_hbufferobject { * 3 = double */ duk_uint8_t elem_type; /* element type */ - duk_uint8_t is_view; + duk_uint8_t is_typedarray; }; -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_uint_t duk_hbufferobject_clamp_bytelength(duk_hbufferobject *h_bufobj, duk_uint_t len); -#endif -DUK_INTERNAL_DECL void duk_hbufferobject_push_validated_read(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size); -DUK_INTERNAL_DECL void duk_hbufferobject_validated_write(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size); +DUK_INTERNAL_DECL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len); +DUK_INTERNAL_DECL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf); +DUK_INTERNAL_DECL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size); +DUK_INTERNAL_DECL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size); +DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx); -#endif /* DUK_HBUFFEROBJECT_H_INCLUDED */ -#line 1 "duk_hthread.h" +#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +/* nothing */ + +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ +#endif /* DUK_HBUFOBJ_H_INCLUDED */ +/* #include duk_hthread.h */ /* * Heap thread object representation. * - * duk_hthread is also the 'context' (duk_context) for exposed APIs - * which mostly operate on the topmost frame of the value stack. + * duk_hthread is also the 'context' for public API functions via a + * different typedef. Most API calls operate on the topmost frame + * of the value stack only. */ -#ifndef DUK_HTHREAD_H_INCLUDED +#if !defined(DUK_HTHREAD_H_INCLUDED) #define DUK_HTHREAD_H_INCLUDED /* * Stack constants */ -#define DUK_VALSTACK_GROW_STEP 128 /* roughly 1 kiB */ -#define DUK_VALSTACK_SHRINK_THRESHOLD 256 /* roughly 2 kiB */ -#define DUK_VALSTACK_SHRINK_SPARE 64 /* roughly 0.5 kiB */ -#define DUK_VALSTACK_INITIAL_SIZE 128 /* roughly 1.0 kiB -> but rounds up to DUK_VALSTACK_GROW_STEP in practice */ -#define DUK_VALSTACK_INTERNAL_EXTRA 64 /* internal extra elements assumed on function entry, - * always added to user-defined 'extra' for e.g. the - * duk_check_stack() call. - */ -#define DUK_VALSTACK_API_ENTRY_MINIMUM DUK_API_ENTRY_STACK - /* number of elements guaranteed to be user accessible - * (in addition to call arguments) on Duktape/C function entry. - */ +/* Initial valstack size, roughly 0.7kiB. */ +#define DUK_VALSTACK_INITIAL_SIZE 96U -/* Note: DUK_VALSTACK_INITIAL_SIZE must be >= DUK_VALSTACK_API_ENTRY_MINIMUM - * + DUK_VALSTACK_INTERNAL_EXTRA so that the initial stack conforms to spare - * requirements. +/* Internal extra elements assumed on function entry, always added to + * user-defined 'extra' for e.g. the duk_check_stack() call. */ +#define DUK_VALSTACK_INTERNAL_EXTRA 32U -#define DUK_VALSTACK_DEFAULT_MAX 1000000L - -#define DUK_CALLSTACK_GROW_STEP 8 /* roughly 256 bytes */ -#define DUK_CALLSTACK_SHRINK_THRESHOLD 16 /* roughly 512 bytes */ -#define DUK_CALLSTACK_SHRINK_SPARE 8 /* roughly 256 bytes */ -#define DUK_CALLSTACK_INITIAL_SIZE 8 -#define DUK_CALLSTACK_DEFAULT_MAX 10000L - -#define DUK_CATCHSTACK_GROW_STEP 4 /* roughly 64 bytes */ -#define DUK_CATCHSTACK_SHRINK_THRESHOLD 8 /* roughly 128 bytes */ -#define DUK_CATCHSTACK_SHRINK_SPARE 4 /* roughly 64 bytes */ -#define DUK_CATCHSTACK_INITIAL_SIZE 4 -#define DUK_CATCHSTACK_DEFAULT_MAX 10000L +/* Number of elements guaranteed to be user accessible (in addition to call + * arguments) on Duktape/C function entry. This is the major public API + * commitment. + */ +#define DUK_VALSTACK_API_ENTRY_MINIMUM DUK_API_ENTRY_STACK /* * Activation defines */ -#define DUK_ACT_FLAG_STRICT (1 << 0) /* function executes in strict mode */ -#define DUK_ACT_FLAG_TAILCALLED (1 << 1) /* activation has tail called one or more times */ -#define DUK_ACT_FLAG_CONSTRUCT (1 << 2) /* function executes as a constructor (called via "new") */ -#define DUK_ACT_FLAG_PREVENT_YIELD (1 << 3) /* activation prevents yield (native call or "new") */ -#define DUK_ACT_FLAG_DIRECT_EVAL (1 << 4) /* activation is a direct eval call */ -#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE (1 << 5) /* activation has active breakpoint(s) */ +#define DUK_ACT_FLAG_STRICT (1U << 0) /* function executes in strict mode */ +#define DUK_ACT_FLAG_TAILCALLED (1U << 1) /* activation has tail called one or more times */ +#define DUK_ACT_FLAG_CONSTRUCT (1U << 2) /* function executes as a constructor (called via "new") */ +#define DUK_ACT_FLAG_PREVENT_YIELD (1U << 3) /* activation prevents yield (native call or "new") */ +#define DUK_ACT_FLAG_DIRECT_EVAL (1U << 4) /* activation is a direct eval call */ +#define DUK_ACT_FLAG_CONSTRUCT_PROXY (1U << 5) /* activation is for Proxy 'construct' call, special return value handling */ +#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE (1U << 6) /* activation has active breakpoint(s) */ -#define DUK_ACT_GET_FUNC(act) ((act)->func) +#define DUK_ACT_GET_FUNC(act) ((act)->func) /* * Flags for __FILE__ / __LINE__ registered into tracedata */ -#define DUK_TB_FLAG_NOBLAME_FILELINE (1 << 0) /* don't report __FILE__ / __LINE__ as fileName/lineNumber */ +#define DUK_TB_FLAG_NOBLAME_FILELINE (1U << 0) /* don't report __FILE__ / __LINE__ as fileName/lineNumber */ /* * Catcher defines */ +/* XXX: remove catcher type entirely */ + /* flags field: LLLLLLFT, L = label (24 bits), F = flags (4 bits), T = type (4 bits) */ #define DUK_CAT_TYPE_MASK 0x0000000fUL #define DUK_CAT_TYPE_BITS 4 @@ -6040,10 +7643,10 @@ DUK_INTERNAL_DECL void duk_hbufferobject_validated_write(duk_context *ctx, duk_h #define DUK_CAT_LABEL_BITS 24 #define DUK_CAT_LABEL_SHIFT 8 -#define DUK_CAT_FLAG_CATCH_ENABLED (1 << 4) /* catch part will catch */ -#define DUK_CAT_FLAG_FINALLY_ENABLED (1 << 5) /* finally part will catch */ -#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED (1 << 6) /* request to create catch binding */ -#define DUK_CAT_FLAG_LEXENV_ACTIVE (1 << 7) /* catch or with binding is currently active */ +#define DUK_CAT_FLAG_CATCH_ENABLED (1U << 4) /* catch part will catch */ +#define DUK_CAT_FLAG_FINALLY_ENABLED (1U << 5) /* finally part will catch */ +#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED (1U << 6) /* request to create catch binding */ +#define DUK_CAT_FLAG_LEXENV_ACTIVE (1U << 7) /* catch or with binding is currently active */ #define DUK_CAT_TYPE_UNKNOWN 0 #define DUK_CAT_TYPE_TCF 1 @@ -6100,8 +7703,6 @@ DUK_INTERNAL_DECL void duk_hbufferobject_validated_write(duk_context *ctx, duk_h #endif #endif /* DUK_USE_ROM_STRINGS */ -#define DUK_HTHREAD_GET_CURRENT_ACTIVATION(thr) (&(thr)->callstack[(thr)->callstack_top - 1]) - /* values for the state field */ #define DUK_HTHREAD_STATE_INACTIVE 1 /* thread not currently running */ #define DUK_HTHREAD_STATE_RUNNING 2 /* thread currently running (only one at a time) */ @@ -6126,42 +7727,62 @@ DUK_INTERNAL_DECL void duk_hbufferobject_validated_write(duk_context *ctx, duk_h * diagnose behavior so it's worth checking even when the check is not 100%. */ -#if defined(DUK_USE_PREFER_SIZE) -#define DUK_ASSERT_CTX_VSSIZE(ctx) /*nop*/ +#if defined(DUK_USE_ASSERTIONS) +/* Assertions for internals. */ +DUK_INTERNAL_DECL void duk_hthread_assert_valid(duk_hthread *thr); +#define DUK_HTHREAD_ASSERT_VALID(thr) do { duk_hthread_assert_valid((thr)); } while (0) + +/* Assertions for public API calls; a bit stronger. */ +DUK_INTERNAL_DECL void duk_ctx_assert_valid(duk_hthread *thr); +#define DUK_CTX_ASSERT_VALID(thr) do { duk_ctx_assert_valid((thr)); } while (0) #else -#define DUK_ASSERT_CTX_VSSIZE(ctx) \ - DUK_ASSERT((duk_size_t) (((duk_hthread *) (ctx))->valstack_end - ((duk_hthread *) (ctx))->valstack) == \ - ((duk_hthread *) (ctx))->valstack_size) +#define DUK_HTHREAD_ASSERT_VALID(thr) do {} while (0) +#define DUK_CTX_ASSERT_VALID(thr) do {} while (0) #endif -#define DUK_ASSERT_CTX_VALID(ctx) do { \ - DUK_ASSERT((ctx) != NULL); \ - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) (ctx)) == DUK_HTYPE_OBJECT); \ - DUK_ASSERT(DUK_HOBJECT_IS_THREAD((duk_hobject *) (ctx))); \ - DUK_ASSERT(((duk_hthread *) (ctx))->unused1 == 0); \ - DUK_ASSERT(((duk_hthread *) (ctx))->unused2 == 0); \ - DUK_ASSERT(((duk_hthread *) (ctx))->valstack != NULL); \ - DUK_ASSERT(((duk_hthread *) (ctx))->valstack_end >= ((duk_hthread *) (ctx))->valstack); \ - DUK_ASSERT(((duk_hthread *) (ctx))->valstack_top >= ((duk_hthread *) (ctx))->valstack); \ - DUK_ASSERT(((duk_hthread *) (ctx))->valstack_top >= ((duk_hthread *) (ctx))->valstack_bottom); \ - DUK_ASSERT(((duk_hthread *) (ctx))->valstack_end >= ((duk_hthread *) (ctx))->valstack_top); \ - DUK_ASSERT_CTX_VSSIZE((ctx)); \ + +/* Assertions for API call entry specifically. Checks 'ctx' but also may + * check internal state (e.g. not in a debugger transport callback). + */ +#define DUK_ASSERT_API_ENTRY(thr) do { \ + DUK_CTX_ASSERT_VALID((thr)); \ + DUK_ASSERT((thr)->heap != NULL); \ + DUK_ASSERT((thr)->heap->dbg_calling_transport == 0); \ } while (0) +/* + * Assertion helpers. + */ + +#define DUK_ASSERT_STRIDX_VALID(val) \ + DUK_ASSERT((duk_uint_t) (val) < DUK_HEAP_NUM_STRINGS) + +#define DUK_ASSERT_BIDX_VALID(val) \ + DUK_ASSERT((duk_uint_t) (val) < DUK_NUM_BUILTINS) + +/* + * Misc + */ + +/* Fast access to 'this' binding. Assumes there's a call in progress. */ +#define DUK_HTHREAD_THIS_PTR(thr) \ + (DUK_ASSERT_EXPR((thr) != NULL), \ + DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \ + (thr)->valstack_bottom - 1) + /* * Struct defines */ -/* XXX: for a memory-code tradeoff, remove 'func' and make it's access either a function - * or a macro. This would make the activation 32 bytes long on 32-bit platforms again. - */ - -/* Note: it's nice if size is 2^N (at least for 32-bit platforms). */ +/* Fields are ordered for alignment/packing. */ struct duk_activation { duk_tval tv_func; /* borrowed: full duk_tval for function being executed; for lightfuncs */ duk_hobject *func; /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL for lightfuncs */ + duk_activation *parent; /* previous (parent) activation (or NULL if none) */ duk_hobject *var_env; /* current variable environment (may be NULL if delayed) */ duk_hobject *lex_env; /* current lexical environment (may be NULL if delayed) */ -#ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY + duk_catcher *cat; /* current catcher (or NULL) */ + +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) /* Previous value of 'func' caller, restored when unwound. Only in use * when 'func' is non-strict. */ @@ -6169,52 +7790,61 @@ struct duk_activation { #endif duk_instr_t *curr_pc; /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_uint32_t prev_line; /* needed for stepping */ -#endif - duk_small_uint_t flags; - /* idx_bottom and idx_retval are only used for book-keeping of - * Ecmascript-initiated calls, to allow returning to an Ecmascript - * function properly. They are duk_size_t to match the convention - * that value stack sizes are duk_size_t and local frame indices - * are duk_idx_t. + /* bottom_byteoff and retval_byteoff are only used for book-keeping + * of ECMAScript-initiated calls, to allow returning to an ECMAScript + * function properly. */ /* Bottom of valstack for this activation, used to reset - * valstack_bottom on return; index is absolute. Note: - * idx_top not needed because top is set to 'nregs' always - * when returning to an Ecmascript activation. + * valstack_bottom on return; offset is absolute. There's + * no need to track 'top' because native call handling deals + * with that using locals, and for ECMAScript returns 'nregs' + * indicates the necessary top. */ - duk_size_t idx_bottom; + duk_size_t bottom_byteoff; /* Return value when returning to this activation (points to caller - * reg, not callee reg); index is absolute (only set if activation is + * reg, not callee reg); offset is absolute (only set if activation is * not topmost). * - * Note: idx_bottom is always set, while idx_retval is only applicable - * for activations below the topmost one. Currently idx_retval for - * the topmost activation is considered garbage (and it not initialized - * on entry or cleared on return; may contain previous or garbage - * values). + * Note: bottom_byteoff is always set, while retval_byteoff is only + * applicable for activations below the topmost one. Currently + * retval_byteoff for the topmost activation is considered garbage + * (and it not initialized on entry or cleared on return; may contain + * previous or garbage values). */ - duk_size_t idx_retval; + duk_size_t retval_byteoff; - /* Current 'this' binding is the value just below idx_bottom. + /* Current 'this' binding is the value just below bottom. * Previously, 'this' binding was handled with an index to the * (calling) valstack. This works for everything except tail - * calls, which must not "cumulate" valstack temps. + * calls, which must not "accumulate" valstack temps. */ + + /* Value stack reserve (valstack_end) byte offset to be restored + * when returning to this activation. Only used by the bytecode + * executor. + */ + duk_size_t reserve_byteoff; + +#if defined(DUK_USE_DEBUGGER_SUPPORT) + duk_uint32_t prev_line; /* needed for stepping */ +#endif + + duk_small_uint_t flags; }; -/* Note: it's nice if size is 2^N (not 4x4 = 16 bytes on 32 bit) */ struct duk_catcher { + duk_catcher *parent; /* previous (parent) catcher (or NULL if none) */ duk_hstring *h_varname; /* borrowed reference to catch variable name (or NULL if none) */ /* (reference is valid as long activation exists) */ duk_instr_t *pc_base; /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */ - duk_size_t callstack_index; /* callstack index of related activation */ duk_size_t idx_base; /* idx_base and idx_base+1 get completion value and type */ duk_uint32_t flags; /* type and control flags, label number */ + /* XXX: could pack 'flags' and 'idx_base' to same value in practice, + * on 32-bit targets this would make duk_catcher 16 bytes. + */ }; struct duk_hthread { @@ -6239,39 +7869,49 @@ struct duk_hthread { duk_uint8_t unused1; duk_uint8_t unused2; - /* Sanity limits for stack sizes. */ - duk_size_t valstack_max; - duk_size_t callstack_max; - duk_size_t catchstack_max; - - /* XXX: Valstack, callstack, and catchstack are currently assumed - * to have non-NULL pointers. Relaxing this would not lead to big - * benefits (except perhaps for terminated threads). + /* XXX: Valstack and callstack are currently assumed to have non-NULL + * pointers. Relaxing this would not lead to big benefits (except + * perhaps for terminated threads). */ - /* Value stack: these are expressed as pointers for faster stack manipulation. - * [valstack,valstack_top[ is GC-reachable, [valstack_top,valstack_end[ is - * not GC-reachable but kept initialized as 'undefined'. + /* Value stack: these are expressed as pointers for faster stack + * manipulation. [valstack,valstack_top[ is GC-reachable, + * [valstack_top,valstack_alloc_end[ is not GC-reachable but kept + * initialized as 'undefined'. [valstack,valstack_end[ is the + * guaranteed/reserved space and the valstack cannot be resized to + * a smaller size. [valstack_end,valstack_alloc_end[ is currently + * allocated slack that can be used to grow the current guaranteed + * space but may be shrunk away without notice. + * + * + * <----------------------- guaranteed ---> + * <---- slack ---> + * <--- frame ---> + * .-------------+=============+----------+--------------. + * |xxxxxxxxxxxxx|yyyyyyyyyyyyy|uuuuuuuuuu|uuuuuuuuuuuuuu| + * `-------------+=============+----------+--------------' + * + * ^ ^ ^ ^ ^ + * | | | | | + * valstack bottom top end alloc_end + * + * xxx = arbitrary values, below current frame + * yyy = arbitrary values, inside current frame + * uuu = outside active value stack, initialized to 'undefined' */ duk_tval *valstack; /* start of valstack allocation */ - duk_tval *valstack_end; /* end of valstack allocation (exclusive) */ + duk_tval *valstack_end; /* end of valstack reservation/guarantee (exclusive) */ + duk_tval *valstack_alloc_end; /* end of valstack allocation */ duk_tval *valstack_bottom; /* bottom of current frame */ duk_tval *valstack_top; /* top of current frame (exclusive) */ -#if !defined(DUK_USE_PREFER_SIZE) - duk_size_t valstack_size; /* cached: valstack_end - valstack (in entries, not bytes) */ -#endif - /* Call stack. [0,callstack_top[ is GC reachable. */ - duk_activation *callstack; - duk_size_t callstack_size; /* allocation size */ - duk_size_t callstack_top; /* next to use, highest used is top - 1 */ + /* Call stack, represented as a linked list starting from the current + * activation (or NULL if nothing is active). + */ + duk_activation *callstack_curr; /* current activation (or NULL if none) */ + duk_size_t callstack_top; /* number of activation records in callstack (0 if none) */ duk_size_t callstack_preventcount; /* number of activation records in callstack preventing a yield */ - /* Catch stack. [0,catchstack_top[ is GC reachable. */ - duk_catcher *catchstack; - duk_size_t catchstack_size; /* allocation size */ - duk_size_t catchstack_top; /* next to use, highest used is top - 1 */ - /* Yield/resume book-keeping. */ duk_hthread *resumer; /* who resumed us (if any) */ @@ -6324,17 +7964,22 @@ DUK_INTERNAL_DECL void duk_hthread_create_builtin_objects(duk_hthread *thr); DUK_INTERNAL_DECL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr); DUK_INTERNAL_DECL void duk_hthread_terminate(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_callstack_grow(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_callstack_shrink_check(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_top); -DUK_INTERNAL_DECL void duk_hthread_catchstack_grow(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_catchstack_shrink_check(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new_top); +DUK_INTERNAL_DECL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act); +DUK_INTERNAL_DECL void duk_hthread_activation_unwind_norz(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr); +DUK_INTERNAL_DECL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level); + +DUK_INTERNAL_DECL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat); +DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act); +DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act); + +#if defined(DUK_USE_FINALIZER_TORTURE) +DUK_INTERNAL_DECL void duk_hthread_valstack_torture_realloc(duk_hthread *thr); +#endif -DUK_INTERNAL_DECL duk_activation *duk_hthread_get_current_activation(duk_hthread *thr); DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ -DUK_INTERNAL_DECL void *duk_hthread_get_callstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ -DUK_INTERNAL_DECL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud); /* indirect allocs */ #if defined(DUK_USE_DEBUGGER_SUPPORT) DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act); @@ -6344,7 +7989,102 @@ DUK_INTERNAL_DECL void duk_hthread_sync_currpc(duk_hthread *thr); DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); #endif /* DUK_HTHREAD_H_INCLUDED */ -#line 1 "duk_hbuffer.h" +/* #include duk_harray.h */ +/* + * Array object representation, used for actual Array instances. + * + * All objects with the exotic array behavior (which must coincide with having + * internal class array) MUST be duk_harrays. No other object can be a + * duk_harray. However, duk_harrays may not always have an array part. + */ + +#if !defined(DUK_HARRAY_H_INCLUDED) +#define DUK_HARRAY_H_INCLUDED + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_harray_assert_valid(duk_harray *h); +#define DUK_HARRAY_ASSERT_VALID(h) do { duk_harray_assert_valid((h)); } while (0) +#else +#define DUK_HARRAY_ASSERT_VALID(h) do {} while (0) +#endif + +#define DUK_HARRAY_LENGTH_WRITABLE(h) (!(h)->length_nonwritable) +#define DUK_HARRAY_LENGTH_NONWRITABLE(h) ((h)->length_nonwritable) +#define DUK_HARRAY_SET_LENGTH_WRITABLE(h) do { (h)->length_nonwritable = 0; } while (0) +#define DUK_HARRAY_SET_LENGTH_NONWRITABLE(h) do { (h)->length_nonwritable = 1; } while (0) + +struct duk_harray { + /* Shared object part. */ + duk_hobject obj; + + /* Array .length. + * + * At present Array .length may be smaller, equal, or even larger + * than the allocated underlying array part. Fast path code must + * always take this into account carefully. + */ + duk_uint32_t length; + + /* Array .length property attributes. The property is always + * non-enumerable and non-configurable. It's initially writable + * but per Object.defineProperty() rules it can be made non-writable + * even if it is non-configurable. Thus we need to track the + * writability explicitly. + * + * XXX: this field to be eliminated and moved into duk_hobject + * flags field to save space. + */ + duk_bool_t length_nonwritable; +}; + +#endif /* DUK_HARRAY_H_INCLUDED */ +/* #include duk_henv.h */ +/* + * Environment object representation. + */ + +#if !defined(DUK_HENV_H_INCLUDED) +#define DUK_HENV_H_INCLUDED + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hdecenv_assert_valid(duk_hdecenv *h); +DUK_INTERNAL_DECL void duk_hobjenv_assert_valid(duk_hobjenv *h); +#define DUK_HDECENV_ASSERT_VALID(h) do { duk_hdecenv_assert_valid((h)); } while (0) +#define DUK_HOBJENV_ASSERT_VALID(h) do { duk_hobjenv_assert_valid((h)); } while (0) +#else +#define DUK_HDECENV_ASSERT_VALID(h) do {} while (0) +#define DUK_HOBJENV_ASSERT_VALID(h) do {} while (0) +#endif + +struct duk_hdecenv { + /* Shared object part. */ + duk_hobject obj; + + /* These control variables provide enough information to access live + * variables for a closure that is still open. If thread == NULL, + * the record is closed and the identifiers are in the property table. + */ + duk_hthread *thread; + duk_hobject *varmap; + duk_size_t regbase_byteoff; +}; + +struct duk_hobjenv { + /* Shared object part. */ + duk_hobject obj; + + /* Target object and 'this' binding for object binding. */ + duk_hobject *target; + + /* The 'target' object is used as a this binding in only some object + * environments. For example, the global environment does not provide + * a this binding, but a with statement does. + */ + duk_bool_t has_this; +}; + +#endif /* DUK_HENV_H_INCLUDED */ +/* #include duk_hbuffer.h */ /* * Heap buffer representation. * @@ -6356,7 +8096,7 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); * The data pointer for a variable size buffer of zero size may be NULL. */ -#ifndef DUK_HBUFFER_H_INCLUDED +#if !defined(DUK_HBUFFER_H_INCLUDED) #define DUK_HBUFFER_H_INCLUDED /* @@ -6402,9 +8142,6 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); * Field access */ -/* Get/set the current user visible size, without accounting for a dynamic - * buffer's "spare" (= usable size). - */ #if defined(DUK_USE_BUFLEN16) /* size stored in duk_heaphdr unused flag bits */ #define DUK_HBUFFER_GET_SIZE(x) ((x)->hdr.h_flags >> 16) @@ -6444,7 +8181,7 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); #define DUK_HBUFFER_EXTERNAL_GET_SIZE(x) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) (x)) #define DUK_HBUFFER_EXTERNAL_SET_SIZE(x,v) DUK_HBUFFER_SET_SIZE((duk_hbuffer *) (x), (v)) -#define DUK_HBUFFER_FIXED_GET_DATA_PTR(heap,x) ((duk_uint8_t *) (((duk_hbuffer_fixed *) (x)) + 1)) +#define DUK_HBUFFER_FIXED_GET_DATA_PTR(heap,x) ((duk_uint8_t *) (((duk_hbuffer_fixed *) (void *) (x)) + 1)) #if defined(DUK_USE_HEAPPTR16) #define DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap,x) \ @@ -6499,7 +8236,7 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); DUK_HBUFFER_EXTERNAL_GET_DATA_PTR((heap), (duk_hbuffer_external *) (x)) : \ DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) \ ) : \ - DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (x)) \ + DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x)) \ ) #else /* Without heap pointer compression duk_hbuffer_dynamic and duk_hbuffer_external @@ -6508,10 +8245,18 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); #define DUK_HBUFFER_GET_DATA_PTR(heap,x) ( \ DUK_HBUFFER_HAS_DYNAMIC((x)) ? \ DUK_HBUFFER_DYNAMIC_GET_DATA_PTR((heap), (duk_hbuffer_dynamic *) (x)) : \ - DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (x)) \ + DUK_HBUFFER_FIXED_GET_DATA_PTR((heap), (duk_hbuffer_fixed *) (void *) (x)) \ ) #endif +/* Validity assert. */ +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hbuffer_assert_valid(duk_hbuffer *h); +#define DUK_HBUFFER_ASSERT_VALID(h) do { duk_hbuffer_assert_valid((h)); } while (0) +#else +#define DUK_HBUFFER_ASSERT_VALID(h) do {} while (0) +#endif + /* * Structs */ @@ -6524,7 +8269,7 @@ struct duk_hbuffer { * it is useful for writing robust native code. */ - /* Current size (not counting a dynamic buffer's "spare"). */ + /* Current size. */ #if defined(DUK_USE_BUFLEN16) /* Stored in duk_heaphdr unused flags. */ #else @@ -6582,7 +8327,10 @@ struct duk_hbuffer_fixed { #if (DUK_USE_ALIGN_BY == 4) duk_uint32_t dummy_for_align4; #elif (DUK_USE_ALIGN_BY == 8) - duk_double_t dummy_for_align8; + duk_double_t dummy_for_align8_1; +#if defined(DUK_USE_64BIT_OPS) + duk_uint64_t dummy_for_align8_2; +#endif #elif (DUK_USE_ALIGN_BY == 1) /* no extra padding */ #else @@ -6673,7 +8421,34 @@ DUK_INTERNAL_DECL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf); #endif /* DUK_HBUFFER_H_INCLUDED */ -#line 1 "duk_heap.h" +/* #include duk_hproxy.h */ +/* + * Proxy object representation. + */ + +#if !defined(DUK_HPROXY_H_INCLUDED) +#define DUK_HPROXY_H_INCLUDED + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_hproxy_assert_valid(duk_hproxy *h); +#define DUK_HPROXY_ASSERT_VALID(h) do { duk_hproxy_assert_valid((h)); } while (0) +#else +#define DUK_HPROXY_ASSERT_VALID(h) do {} while (0) +#endif + +struct duk_hproxy { + /* Shared object part. */ + duk_hobject obj; + + /* Proxy target object. */ + duk_hobject *target; + + /* Proxy handlers (traps). */ + duk_hobject *handler; +}; + +#endif /* DUK_HPROXY_H_INCLUDED */ +/* #include duk_heap.h */ /* * Heap structure. * @@ -6681,7 +8456,7 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * strings for one or more threads. */ -#ifndef DUK_HEAP_H_INCLUDED +#if !defined(DUK_HEAP_H_INCLUDED) #define DUK_HEAP_H_INCLUDED /* alloc function typedefs in duktape.h */ @@ -6690,12 +8465,10 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * Heap flags */ -#define DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING (1 << 0) /* mark-and-sweep is currently running */ -#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED (1 << 1) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */ -#define DUK_HEAP_FLAG_REFZERO_FREE_RUNNING (1 << 2) /* refcount code is processing refzero list */ -#define DUK_HEAP_FLAG_ERRHANDLER_RUNNING (1 << 3) /* an error handler (user callback to augment/replace error) is running */ -#define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1 << 4) /* executor interrupt running (used to avoid nested interrupts) */ -#define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1 << 5) /* heap destruction ongoing, finalizer rescue no longer possible */ +#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED (1U << 0) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */ +#define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1U << 1) /* executor interrupt running (used to avoid nested interrupts) */ +#define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1U << 2) /* heap destruction ongoing, finalizer rescue no longer possible */ +#define DUK_HEAP_FLAG_DEBUGGER_PAUSED (1U << 3) /* debugger is paused: talk with debug client until step/resume */ #define DUK__HEAP_HAS_FLAGS(heap,bits) ((heap)->flags & (bits)) #define DUK__HEAP_SET_FLAGS(heap,bits) do { \ @@ -6705,26 +8478,20 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * (heap)->flags &= ~(bits); \ } while (0) -#define DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) -#define DUK_HEAP_HAS_ERRHANDLER_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING) #define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) +#define DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) -#define DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) -#define DUK_HEAP_SET_ERRHANDLER_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING) #define DUK_HEAP_SET_INTERRUPT_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_SET_FINALIZER_NORESCUE(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) +#define DUK_HEAP_SET_DEBUGGER_PAUSED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) -#define DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) -#define DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING) #define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) +#define DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) /* * Longjmp types, also double as identifying continuation type for a rethrow (in 'finally') @@ -6747,11 +8514,22 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * field and the GC caller can impose further flags. */ -#define DUK_MS_FLAG_EMERGENCY (1 << 0) /* emergency mode: try extra hard */ -#define DUK_MS_FLAG_NO_STRINGTABLE_RESIZE (1 << 1) /* don't resize stringtable (but may sweep it); needed during stringtable resize */ -#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1 << 2) /* don't compact objects; needed during object property allocation resize */ -#define DUK_MS_FLAG_NO_FINALIZERS (1 << 3) /* don't run finalizers; leave finalizable objects in finalize_list for next round */ -#define DUK_MS_FLAG_SKIP_FINALIZERS (1 << 4) /* don't run finalizers; queue finalizable objects back to heap_allocated */ +/* Emergency mark-and-sweep: try extra hard, even at the cost of + * performance. + */ +#define DUK_MS_FLAG_EMERGENCY (1U << 0) + +/* Postpone rescue decisions for reachable objects with FINALIZED set. + * Used during finalize_list processing to avoid incorrect rescue + * decisions due to finalize_list being a reachability root. + */ +#define DUK_MS_FLAG_POSTPONE_RESCUE (1U << 1) + +/* Don't compact objects; needed during object property table resize + * to prevent a recursive resize. It would suffice to protect only the + * current object being resized, but this is not yet implemented. + */ +#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1U << 2) /* * Thread switching @@ -6769,6 +8547,18 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * } while (0) #endif +/* + * Stats + */ + +#if defined(DUK_USE_DEBUG) +#define DUK_STATS_INC(heap,fieldname) do { \ + (heap)->fieldname += 1; \ + } while (0) +#else +#define DUK_STATS_INC(heap,fieldname) do {} while (0) +#endif + /* * Other heap related defines */ @@ -6783,7 +8573,6 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * GC is skipped because there is no thread do it with yet (happens * only during init phases). */ -#if defined(DUK_USE_MARK_AND_SWEEP) #if defined(DUK_USE_REFERENCE_COUNTING) #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT 12800L /* 50x heap size */ #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L @@ -6793,6 +8582,12 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L #endif + +/* GC torture. */ +#if defined(DUK_USE_GC_TORTURE) +#define DUK_GC_TORTURE(heap) do { duk_heap_mark_and_sweep((heap), 0); } while (0) +#else +#define DUK_GC_TORTURE(heap) do { } while (0) #endif /* Stringcache is used for speeding up char-offset-to-byte-offset @@ -6801,33 +8596,15 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * #define DUK_HEAP_STRCACHE_SIZE 4 #define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT 16 /* strings up to the this length are not cached */ -/* helper to insert a (non-string) heap object into heap allocated list */ -#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap),(hdr)) - -/* - * Stringtable - */ - -/* initial stringtable size, must be prime and higher than DUK_UTIL_MIN_HASH_PRIME */ -#define DUK_STRTAB_INITIAL_SIZE 17 - -/* indicates a deleted string; any fixed non-NULL, non-hstring pointer works */ -#define DUK_STRTAB_DELETED_MARKER(heap) ((duk_hstring *) heap) - -/* resizing parameters */ -#define DUK_STRTAB_MIN_FREE_DIVISOR 4 /* load factor max 75% */ -#define DUK_STRTAB_MIN_USED_DIVISOR 4 /* load factor min 25% */ -#define DUK_STRTAB_GROW_ST_SIZE(n) ((n) + (n)) /* used entries + approx 100% -> reset load to 50% */ - -#define DUK_STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */ -#define DUK_STRTAB_HIGHEST_32BIT_PRIME 0xfffffffbUL - -/* probe sequence (open addressing) */ -#define DUK_STRTAB_HASH_INITIAL(hash,h_size) ((hash) % (h_size)) -#define DUK_STRTAB_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash)) - -/* fixed top level hashtable size (separate chaining) */ -#define DUK_STRTAB_CHAIN_SIZE DUK_USE_STRTAB_CHAIN_SIZE +/* Some list management macros. */ +#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap), (hdr)) +#if defined(DUK_USE_REFERENCE_COUNTING) +#define DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap,hdr) duk_heap_remove_from_heap_allocated((heap), (hdr)) +#endif +#if defined(DUK_USE_FINALIZER_SUPPORT) +#define DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap,hdr) duk_heap_insert_into_finalize_list((heap), (hdr)) +#define DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap,hdr) duk_heap_remove_from_finalize_list((heap), (hdr)) +#endif /* * Built-in strings @@ -6897,11 +8674,22 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); #define DUK_REALLOC_INDIRECT(heap,cb,ud,newsize) duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize)) #define DUK_FREE(heap,ptr) duk_heap_mem_free((heap), (ptr)) +/* + * Checked allocation, relative to a thread + * + * DUK_FREE_CHECKED() doesn't actually throw, but accepts a 'thr' argument + * for convenience. + */ + +#define DUK_ALLOC_CHECKED(thr,size) duk_heap_mem_alloc_checked((thr), (size)) +#define DUK_ALLOC_CHECKED_ZEROED(thr,size) duk_heap_mem_alloc_checked_zeroed((thr), (size)) +#define DUK_FREE_CHECKED(thr,ptr) duk_heap_mem_free((thr)->heap, (ptr)) + /* * Memory constants */ -#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 5 /* Retry allocation after mark-and-sweep for this +#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 10 /* Retry allocation after mark-and-sweep for this * many times. A single mark-and-sweep round is * not guaranteed to free all unreferenced memory * because of finalization (in fact, ANY number of @@ -6931,38 +8719,20 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); /* Milliseconds between status notify and transport peeks. */ #define DUK_HEAP_DBG_RATELIMIT_MILLISECS 200 -/* Step types */ -#define DUK_STEP_TYPE_NONE 0 -#define DUK_STEP_TYPE_INTO 1 -#define DUK_STEP_TYPE_OVER 2 -#define DUK_STEP_TYPE_OUT 3 +/* Debugger pause flags. */ +#define DUK_PAUSE_FLAG_ONE_OPCODE (1U << 0) /* pause when a single opcode has been executed */ +#define DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE (1U << 1) /* one opcode pause actually active; artifact of current implementation */ +#define DUK_PAUSE_FLAG_LINE_CHANGE (1U << 2) /* pause when current line number changes */ +#define DUK_PAUSE_FLAG_FUNC_ENTRY (1U << 3) /* pause when entering a function */ +#define DUK_PAUSE_FLAG_FUNC_EXIT (1U << 4) /* pause when exiting current function */ +#define DUK_PAUSE_FLAG_CAUGHT_ERROR (1U << 5) /* pause when about to throw an error that is caught */ +#define DUK_PAUSE_FLAG_UNCAUGHT_ERROR (1U << 6) /* pause when about to throw an error that won't be caught */ struct duk_breakpoint { duk_hstring *filename; duk_uint32_t line; }; -#if defined(DUK_USE_DEBUGGER_SUPPORT) -#define DUK_HEAP_IS_DEBUGGER_ATTACHED(heap) ((heap)->dbg_read_cb != NULL) -#define DUK_HEAP_CLEAR_STEP_STATE(heap) do { \ - (heap)->dbg_step_type = DUK_STEP_TYPE_NONE; \ - (heap)->dbg_step_thread = NULL; \ - (heap)->dbg_step_csindex = 0; \ - (heap)->dbg_step_startline = 0; \ - } while (0) -#define DUK_HEAP_SET_PAUSED(heap) do { \ - (heap)->dbg_paused = 1; \ - (heap)->dbg_state_dirty = 1; \ - DUK_HEAP_CLEAR_STEP_STATE((heap)); \ - } while (0) -#define DUK_HEAP_CLEAR_PAUSED(heap) do { \ - (heap)->dbg_paused = 0; \ - (heap)->dbg_state_dirty = 1; \ - DUK_HEAP_CLEAR_STEP_STATE((heap)); \ - } while (0) -#define DUK_HEAP_IS_PAUSED(heap) ((heap)->dbg_paused) -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - /* * String cache should ideally be at duk_hthread level, but that would * cause string finalization to slow down relative to the number of @@ -6972,7 +8742,7 @@ struct duk_breakpoint { * Thus, string caches are now at the heap level now. */ -struct duk_strcache { +struct duk_strcache_entry { duk_hstring *h; duk_uint32_t bidx; duk_uint32_t cidx; @@ -6991,33 +8761,38 @@ struct duk_ljstate { duk_tval value2; /* 2nd related value (type specific) */ }; +#define DUK_ASSERT_LJSTATE_UNSET(heap) do { \ + DUK_ASSERT(heap != NULL); \ + DUK_ASSERT(heap->lj.type == DUK_LJ_TYPE_UNKNOWN); \ + DUK_ASSERT(heap->lj.iserror == 0); \ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value1)); \ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value2)); \ + } while (0) +#define DUK_ASSERT_LJSTATE_SET(heap) do { \ + DUK_ASSERT(heap != NULL); \ + DUK_ASSERT(heap->lj.type != DUK_LJ_TYPE_UNKNOWN); \ + } while (0) + /* - * Stringtable entry for fixed size stringtable + * Literal intern cache */ -struct duk_strtab_entry { -#if defined(DUK_USE_HEAPPTR16) - /* A 16-bit listlen makes sense with 16-bit heap pointers: there - * won't be space for 64k strings anyway. - */ - duk_uint16_t listlen; /* if 0, 'str16' used, if > 0, 'strlist16' used */ - union { - duk_uint16_t strlist16; - duk_uint16_t str16; - } u; -#else - duk_size_t listlen; /* if 0, 'str' used, if > 0, 'strlist' used */ - union { - duk_hstring **strlist; - duk_hstring *str; - } u; -#endif +struct duk_litcache_entry { + const duk_uint8_t *addr; + duk_hstring *h; }; /* * Main heap structure */ +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL void duk_heap_assert_valid(duk_heap *heap); +#define DUK_HEAP_ASSERT_VALID(heap) do { duk_heap_assert_valid((heap)); } while (0) +#else +#define DUK_HEAP_ASSERT_VALID(heap) do {} while (0) +#endif + struct duk_heap { duk_small_uint_t flags; @@ -7027,16 +8802,10 @@ struct duk_heap { duk_free_function free_func; /* Heap udata, used for allocator functions but also for other heap - * level callbacks like pointer compression, etc. + * level callbacks like fatal function, pointer compression, etc. */ void *heap_udata; - /* Precomputed pointers when using 16-bit heap pointer packing. */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t heapptr_null16; - duk_uint16_t heapptr_deleted16; -#endif - /* Fatal error handling, called e.g. when a longjmp() is needed but * lj.jmpbuf_ptr is NULL. fatal_func must never return; it's not * declared as "noreturn" because doing that for typedefs is a bit @@ -7044,56 +8813,149 @@ struct duk_heap { */ duk_fatal_function fatal_func; - /* allocated heap objects */ + /* Main list of allocated heap objects. Objects are either here, + * in finalize_list waiting for processing, or in refzero_list + * temporarily while a DECREF refzero cascade finishes. + */ duk_heaphdr *heap_allocated; - /* work list for objects whose refcounts are zero but which have not been - * "finalized"; avoids recursive C calls when refcounts go to zero in a - * chain of objects. + /* Temporary work list for freeing a cascade of objects when a DECREF + * (or DECREF_NORZ) encounters a zero refcount. Using a work list + * allows fixed C stack size when refcounts go to zero for a chain of + * objects. Outside of DECREF this is always a NULL because DECREF is + * processed without side effects (only memory free calls). */ #if defined(DUK_USE_REFERENCE_COUNTING) duk_heaphdr *refzero_list; - duk_heaphdr *refzero_list_tail; #endif -#if defined(DUK_USE_MARK_AND_SWEEP) - /* mark-and-sweep control */ -#if defined(DUK_USE_VOLUNTARY_GC) - duk_int_t mark_and_sweep_trigger_counter; -#endif - duk_int_t mark_and_sweep_recursion_depth; - - /* mark-and-sweep flags automatically active (used for critical sections) */ - duk_small_uint_t mark_and_sweep_base_flags; - - /* work list for objects to be finalized (by mark-and-sweep) */ +#if defined(DUK_USE_FINALIZER_SUPPORT) + /* Work list for objects to be finalized. */ duk_heaphdr *finalize_list; +#if defined(DUK_USE_ASSERTIONS) + /* Object whose finalizer is executing right now (no nesting). */ + duk_heaphdr *currently_finalizing; +#endif #endif - /* longjmp state */ + /* Freelist for duk_activations and duk_catchers. */ +#if defined(DUK_USE_CACHE_ACTIVATION) + duk_activation *activation_free; +#endif +#if defined(DUK_USE_CACHE_CATCHER) + duk_catcher *catcher_free; +#endif + + /* Voluntary mark-and-sweep trigger counter. Intentionally signed + * because we continue decreasing the value when voluntary GC cannot + * run. + */ +#if defined(DUK_USE_VOLUNTARY_GC) + duk_int_t ms_trigger_counter; +#endif + + /* Mark-and-sweep recursion control: too deep recursion causes + * multi-pass processing to avoid growing C stack without bound. + */ + duk_uint_t ms_recursion_depth; + + /* Mark-and-sweep flags automatically active (used for critical sections). */ + duk_small_uint_t ms_base_flags; + + /* Mark-and-sweep running flag. Prevents re-entry, and also causes + * refzero events to be ignored (= objects won't be queued to refzero_list). + * + * 0: mark-and-sweep not running + * 1: mark-and-sweep is running + * 2: heap destruction active or debugger active, prevent mark-and-sweep + * and refzero processing (but mark-and-sweep not itself running) + */ + duk_uint_t ms_running; + + /* Mark-and-sweep prevent count, stacking. Used to avoid M&S side + * effects (besides finalizers which are controlled separately) such + * as compacting the string table or object property tables. This + * is also bumped when ms_running is set to prevent recursive re-entry. + * Can also be bumped when mark-and-sweep is not running. + */ + duk_uint_t ms_prevent_count; + + /* Finalizer processing prevent count, stacking. Bumped when finalizers + * are processed to prevent recursive finalizer processing (first call site + * processing finalizers handles all finalizers until the list is empty). + * Can also be bumped explicitly to prevent finalizer execution. + */ + duk_uint_t pf_prevent_count; + + /* When processing finalize_list, don't actually run finalizers but + * queue finalizable objects back to heap_allocated as is. This is + * used during heap destruction to deal with finalizers that keep + * on creating more finalizable garbage. + */ + duk_uint_t pf_skip_finalizers; + +#if defined(DUK_USE_ASSERTIONS) + /* Set when we're in a critical path where an error throw would cause + * e.g. sandboxing/protected call violations or state corruption. This + * is just used for asserts. + */ + duk_bool_t error_not_allowed; +#endif + +#if defined(DUK_USE_ASSERTIONS) + /* Set when heap is still being initialized, helps with writing + * some assertions. + */ + duk_bool_t heap_initializing; +#endif + + /* Marker for detecting internal "double faults", errors thrown when + * we're trying to create an error object, see duk_error_throw.c. + */ + duk_bool_t creating_error; + + /* Marker for indicating we're calling a user error augmentation + * (errCreate/errThrow) function. Errors created/thrown during + * such a call are not augmented. + */ +#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE) + duk_bool_t augmenting_error; +#endif + + /* Longjmp state. */ duk_ljstate lj; - /* marker for detecting internal "double faults", see duk_error_throw.c */ - duk_bool_t handling_error; - - /* heap thread, used internally and for finalization */ + /* Heap thread, used internally and for finalization. */ duk_hthread *heap_thread; - /* current thread */ - duk_hthread *curr_thread; /* currently running thread */ + /* Current running thread. */ + duk_hthread *curr_thread; - /* heap level "stash" object (e.g., various reachability roots) */ + /* Heap level "stash" object (e.g., various reachability roots). */ duk_hobject *heap_object; /* duk_handle_call / duk_handle_safe_call recursion depth limiting */ duk_int_t call_recursion_depth; duk_int_t call_recursion_limit; - /* mix-in value for computing string hashes; should be reasonably unpredictable */ + /* Mix-in value for computing string hashes; should be reasonably unpredictable. */ duk_uint32_t hash_seed; - /* rnd_state for duk_util_tinyrandom.c */ - duk_uint32_t rnd_state; + /* Random number state for duk_util_tinyrandom.c. */ +#if !defined(DUK_USE_GET_RANDOM_DOUBLE) +#if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) + duk_uint32_t rnd_state; /* State for Shamir's three-op algorithm */ +#else + duk_uint64_t rnd_state[2]; /* State for xoroshiro128+ */ +#endif +#endif + + /* Counter for unique local symbol creation. */ + /* XXX: When 64-bit types are available, it would be more efficient to + * use a duk_uint64_t at least for incrementing but maybe also for + * string formatting in the Symbol constructor. + */ + duk_uint32_t sym_counter[2]; /* For manual debugging: instruction count based on executor and * interrupt counter book-keeping. Inspect debug logs to see how @@ -7104,10 +8966,9 @@ struct duk_heap { duk_int_t inst_count_interrupt; #endif - /* debugger */ - + /* Debugger state. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - /* callbacks and udata; dbg_read_cb != NULL is used to indicate attached state */ + /* Callbacks and udata; dbg_read_cb != NULL is used to indicate attached state. */ duk_debug_read_function dbg_read_cb; /* required, NULL implies detached */ duk_debug_write_function dbg_write_cb; /* required */ duk_debug_peek_function dbg_peek_cb; @@ -7117,55 +8978,59 @@ struct duk_heap { duk_debug_detached_function dbg_detached_cb; void *dbg_udata; - /* debugger state, only relevant when attached */ + /* The following are only relevant when debugger is attached. */ duk_bool_t dbg_processing; /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. no breakpoints when processing debugger eval) */ - duk_bool_t dbg_paused; /* currently paused: talk with debug client until step/resume */ duk_bool_t dbg_state_dirty; /* resend state next time executor is about to run */ duk_bool_t dbg_force_restart; /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */ duk_bool_t dbg_detaching; /* debugger detaching; used to avoid calling detach handler recursively */ - duk_small_uint_t dbg_step_type; /* step type: none, step into, step over, step out */ - duk_hthread *dbg_step_thread; /* borrowed; NULL if no step state (NULLed in unwind) */ - duk_size_t dbg_step_csindex; /* callstack index */ - duk_uint32_t dbg_step_startline; /* starting line number */ + duk_small_uint_t dbg_pause_flags; /* flags for automatic pause behavior */ + duk_activation *dbg_pause_act; /* activation related to pause behavior (pause on line change, function entry/exit) */ + duk_uint32_t dbg_pause_startline; /* starting line number for line change related pause behavior */ duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS]; /* breakpoints: [0,breakpoint_count[ gc reachable */ duk_small_uint_t dbg_breakpoint_count; duk_breakpoint *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1]; /* currently active breakpoints: NULL term, borrowed pointers */ /* XXX: make active breakpoints actual copies instead of pointers? */ /* These are for rate limiting Status notifications and transport peeking. */ - duk_uint32_t dbg_exec_counter; /* cumulative opcode execution count (overflows are OK) */ - duk_uint32_t dbg_last_counter; /* value of dbg_exec_counter when we last did a Date-based check */ + duk_uint_t dbg_exec_counter; /* cumulative opcode execution count (overflows are OK) */ + duk_uint_t dbg_last_counter; /* value of dbg_exec_counter when we last did a Date-based check */ duk_double_t dbg_last_time; /* time when status/peek was last done (Date-based rate limit) */ /* Used to support single-byte stream lookahead. */ duk_bool_t dbg_have_next_byte; duk_uint8_t dbg_next_byte; +#endif /* DUK_USE_DEBUGGER_SUPPORT */ +#if defined(DUK_USE_ASSERTIONS) + duk_bool_t dbg_calling_transport; /* transport call in progress, calling into Duktape forbidden */ #endif - /* string intern table (weak refs) */ -#if defined(DUK_USE_STRTAB_PROBE) -#if defined(DUK_USE_HEAPPTR16) + /* String intern table (weak refs). */ +#if defined(DUK_USE_STRTAB_PTRCOMP) duk_uint16_t *strtable16; #else duk_hstring **strtable; #endif - duk_uint32_t st_size; /* alloc size in elements */ - duk_uint32_t st_used; /* used elements (includes DELETED) */ + duk_uint32_t st_mask; /* mask for lookup, st_size - 1 */ + duk_uint32_t st_size; /* stringtable size */ +#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) + duk_uint32_t st_count; /* string count for resize load factor checks */ #endif + duk_bool_t st_resizing; /* string table is being resized; avoid recursive resize */ - /* XXX: static alloc is OK until separate chaining stringtable - * resizing is implemented. - */ -#if defined(DUK_USE_STRTAB_CHAIN) - duk_strtab_entry strtable[DUK_STRTAB_CHAIN_SIZE]; -#endif - - /* string access cache (codepoint offset -> byte offset) for fast string + /* String access cache (codepoint offset -> byte offset) for fast string * character looping; 'weak' reference which needs special handling in GC. */ - duk_strcache strcache[DUK_HEAP_STRCACHE_SIZE]; + duk_strcache_entry strcache[DUK_HEAP_STRCACHE_SIZE]; - /* built-in strings */ +#if defined(DUK_USE_LITCACHE_SIZE) + /* Literal intern cache. When enabled, strings interned as literals + * (e.g. duk_push_literal()) will be pinned and cached for the lifetime + * of the heap. + */ + duk_litcache_entry litcache[DUK_USE_LITCACHE_SIZE]; +#endif + + /* Built-in strings. */ #if defined(DUK_USE_ROM_STRINGS) /* No field needed when strings are in ROM. */ #else @@ -7174,6 +9039,59 @@ struct duk_heap { #else duk_hstring *strs[DUK_HEAP_NUM_STRINGS]; #endif +#endif + + /* Stats. */ +#if defined(DUK_USE_DEBUG) + duk_int_t stats_exec_opcodes; + duk_int_t stats_exec_interrupt; + duk_int_t stats_exec_throw; + duk_int_t stats_call_all; + duk_int_t stats_call_tailcall; + duk_int_t stats_call_ecmatoecma; + duk_int_t stats_safecall_all; + duk_int_t stats_safecall_nothrow; + duk_int_t stats_safecall_throw; + duk_int_t stats_ms_try_count; + duk_int_t stats_ms_skip_count; + duk_int_t stats_ms_emergency_count; + duk_int_t stats_strtab_intern_hit; + duk_int_t stats_strtab_intern_miss; + duk_int_t stats_strtab_resize_check; + duk_int_t stats_strtab_resize_grow; + duk_int_t stats_strtab_resize_shrink; + duk_int_t stats_strtab_litcache_hit; + duk_int_t stats_strtab_litcache_miss; + duk_int_t stats_strtab_litcache_pin; + duk_int_t stats_object_realloc_props; + duk_int_t stats_object_abandon_array; + duk_int_t stats_getownpropdesc_count; + duk_int_t stats_getownpropdesc_hit; + duk_int_t stats_getownpropdesc_miss; + duk_int_t stats_getpropdesc_count; + duk_int_t stats_getpropdesc_hit; + duk_int_t stats_getpropdesc_miss; + duk_int_t stats_getprop_all; + duk_int_t stats_getprop_arrayidx; + duk_int_t stats_getprop_bufobjidx; + duk_int_t stats_getprop_bufferidx; + duk_int_t stats_getprop_bufferlen; + duk_int_t stats_getprop_stringidx; + duk_int_t stats_getprop_stringlen; + duk_int_t stats_getprop_proxy; + duk_int_t stats_getprop_arguments; + duk_int_t stats_putprop_all; + duk_int_t stats_putprop_arrayidx; + duk_int_t stats_putprop_bufobjidx; + duk_int_t stats_putprop_bufferidx; + duk_int_t stats_putprop_proxy; + duk_int_t stats_getvar_all; + duk_int_t stats_putvar_all; + duk_int_t stats_envrec_delayedcreate; + duk_int_t stats_envrec_create; + duk_int_t stats_envrec_newenv; + duk_int_t stats_envrec_oldenv; + duk_int_t stats_envrec_pushclosure; #endif }; @@ -7188,41 +9106,43 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, void *heap_udata, duk_fatal_function fatal_func); DUK_INTERNAL_DECL void duk_heap_free(duk_heap *heap); -DUK_INTERNAL_DECL void duk_free_hobject_inner(duk_heap *heap, duk_hobject *h); -DUK_INTERNAL_DECL void duk_free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_free_hstring_inner(duk_heap *heap, duk_hstring *h); +DUK_INTERNAL_DECL void duk_free_hobject(duk_heap *heap, duk_hobject *h); +DUK_INTERNAL_DECL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h); +DUK_INTERNAL_DECL void duk_free_hstring(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr); DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); +#if defined(DUK_USE_REFERENCE_COUNTING) +DUK_INTERNAL_DECL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); +#endif +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL_DECL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr); +DUK_INTERNAL_DECL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr); +#endif +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr); #endif #if defined(DUK_USE_INTERRUPT_COUNTER) DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr); #endif -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); +#if defined(DUK_USE_LITCACHE_SIZE) +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen); #endif -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val); -#endif -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val); -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); #if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h); +DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h); #endif -#if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE) -DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap); -#endif -DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap); +DUK_INTERNAL_DECL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev); +DUK_INTERNAL_DECL void duk_heap_strtable_force_resize(duk_heap *heap); +DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap); #if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap); +DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap); #endif - DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset); @@ -7234,44 +9154,26 @@ DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr); DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size); +DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size); +DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize); DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize); DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr); -#ifdef DUK_USE_REFERENCE_COUNTING -#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); -#endif -#if 0 /* unused */ -DUK_INTERNAL_DECL void duk_tval_incref_allownull(duk_tval *tv); -#endif -DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); -#if 0 /* unused */ -DUK_INTERNAL_DECL void duk_tval_decref_allownull(duk_hthread *thr, duk_tval *tv); -#endif -#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); -#endif -#if 0 /* unused */ -DUK_INTERNAL_DECL void duk_heaphdr_incref_allownull(duk_heaphdr *h); -#endif -DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_decref_allownull(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr); -#else -/* no refcounting */ -#endif +DUK_INTERNAL_DECL void duk_heap_free_freelists(duk_heap *heap); -#if defined(DUK_USE_MARK_AND_SWEEP) -DUK_INTERNAL_DECL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); -#endif +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL_DECL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj); +DUK_INTERNAL_DECL void duk_heap_process_finalize_list(duk_heap *heap); +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +DUK_INTERNAL_DECL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len); #endif /* DUK_HEAP_H_INCLUDED */ -#line 1 "duk_debugger.h" -#ifndef DUK_DEBUGGER_H_INCLUDED +/* #include duk_debugger.h */ +#if !defined(DUK_DEBUGGER_H_INCLUDED) #define DUK_DEBUGGER_H_INCLUDED /* Debugger protocol version is defined in the public API header. */ @@ -7312,9 +9214,9 @@ DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uin /* Commands and notifys initiated by Duktape. */ #define DUK_DBG_CMD_STATUS 0x01 -#define DUK_DBG_CMD_PRINT 0x02 -#define DUK_DBG_CMD_ALERT 0x03 -#define DUK_DBG_CMD_LOG 0x04 +#define DUK_DBG_CMD_UNUSED_2 0x02 /* Duktape 1.x: print notify */ +#define DUK_DBG_CMD_UNUSED_3 0x03 /* Duktape 1.x: alert notify */ +#define DUK_DBG_CMD_UNUSED_4 0x04 /* Duktape 1.x: log notify */ #define DUK_DBG_CMD_THROW 0x05 #define DUK_DBG_CMD_DETACHING 0x06 #define DUK_DBG_CMD_APPNOTIFY 0x07 @@ -7346,7 +9248,8 @@ DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uin /* The low 8 bits map directly to duk_hobject.h DUK_PROPDESC_FLAG_xxx. * The remaining flags are specific to the debugger. */ -#define DUK_DBG_PROPFLAG_INTERNAL (1 << 8) +#define DUK_DBG_PROPFLAG_SYMBOL (1U << 8) +#define DUK_DBG_PROPFLAG_HIDDEN (1U << 9) #if defined(DUK_USE_DEBUGGER_SUPPORT) DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap); @@ -7412,10 +9315,16 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bo DUK_INTERNAL_DECL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line); DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index); -#endif + +DUK_INTERNAL_DECL duk_bool_t duk_debug_is_attached(duk_heap *heap); +DUK_INTERNAL_DECL duk_bool_t duk_debug_is_paused(duk_heap *heap); +DUK_INTERNAL_DECL void duk_debug_set_paused(duk_heap *heap); +DUK_INTERNAL_DECL void duk_debug_clear_paused(duk_heap *heap); +DUK_INTERNAL_DECL void duk_debug_clear_pause_state(duk_heap *heap); +#endif /* DUK_USE_DEBUGGER_SUPPORT */ #endif /* DUK_DEBUGGER_H_INCLUDED */ -#line 1 "duk_debug.h" +/* #include duk_debug.h */ /* * Debugging macros, DUK_DPRINT() and its variants in particular. * @@ -7438,24 +9347,24 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s * works poorly with threading. */ -#ifndef DUK_DEBUG_H_INCLUDED +#if !defined(DUK_DEBUG_H_INCLUDED) #define DUK_DEBUG_H_INCLUDED -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) -#if defined(DUK_USE_DPRINT) +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) #define DUK_D(x) x #else #define DUK_D(x) do { } while (0) /* omit */ #endif -#if defined(DUK_USE_DDPRINT) +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) #define DUK_DD(x) x #else #define DUK_DD(x) do { } while (0) /* omit */ #endif -#if defined(DUK_USE_DDDPRINT) +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) #define DUK_DDD(x) x #else #define DUK_DDD(x) do { } while (0) /* omit */ @@ -7465,26 +9374,26 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s * Exposed debug macros: debugging enabled */ -#define DUK_LEVEL_DEBUG 1 -#define DUK_LEVEL_DDEBUG 2 -#define DUK_LEVEL_DDDEBUG 3 - -#ifdef DUK_USE_VARIADIC_MACROS +#if defined(DUK_USE_VARIADIC_MACROS) /* Note: combining __FILE__, __LINE__, and __func__ into fmt would be * possible compile time, but waste some space with shared function names. */ -#define DUK__DEBUG_LOG(lev,...) duk_debug_log((duk_small_int_t) (lev), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, DUK_FUNC_MACRO, __VA_ARGS__); +#define DUK__DEBUG_LOG(lev,...) duk_debug_log((duk_int_t) (lev), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, DUK_FUNC_MACRO, __VA_ARGS__); +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) #define DUK_DPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DEBUG, __VA_ARGS__) +#else +#define DUK_DPRINT(...) +#endif -#ifdef DUK_USE_DDPRINT +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) #define DUK_DDPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DDEBUG, __VA_ARGS__) #else #define DUK_DDPRINT(...) #endif -#ifdef DUK_USE_DDDPRINT +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) #define DUK_DDDPRINT(...) DUK__DEBUG_LOG(DUK_LEVEL_DDDEBUG, __VA_ARGS__) #else #define DUK_DDDPRINT(...) @@ -7494,11 +9403,10 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s #define DUK__DEBUG_STASH(lev) \ (void) DUK_SNPRINTF(duk_debug_file_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FILE_MACRO), \ - duk_debug_file_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0; \ - (void) DUK_SNPRINTF(duk_debug_line_stash, DUK_DEBUG_STASH_SIZE, "%ld", (long) DUK_LINE_MACRO), \ - duk_debug_line_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0; \ + (void) (duk_debug_file_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), \ + (void) (duk_debug_line_stash = (duk_int_t) DUK_LINE_MACRO), \ (void) DUK_SNPRINTF(duk_debug_func_stash, DUK_DEBUG_STASH_SIZE, "%s", (const char *) DUK_FUNC_MACRO), \ - duk_debug_func_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0; \ + (void) (duk_debug_func_stash[DUK_DEBUG_STASH_SIZE - 1] = (char) 0), \ (void) (duk_debug_level_stash = (lev)) /* Without variadic macros resort to comma expression trickery to handle debug @@ -7507,19 +9415,19 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s * statement from the compiler. */ -#ifdef DUK_USE_DPRINT +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 0) #define DUK_DPRINT DUK__DEBUG_STASH(DUK_LEVEL_DEBUG), (void) duk_debug_log /* args go here in parens */ #else #define DUK_DPRINT 0 && /* args go here as a comma expression in parens */ #endif -#ifdef DUK_USE_DDPRINT +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) #define DUK_DDPRINT DUK__DEBUG_STASH(DUK_LEVEL_DDEBUG), (void) duk_debug_log /* args go here in parens */ #else #define DUK_DDPRINT 0 && /* args */ #endif -#ifdef DUK_USE_DDDPRINT +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) #define DUK_DDDPRINT DUK__DEBUG_STASH(DUK_LEVEL_DDDEBUG), (void) duk_debug_log /* args go here in parens */ #else #define DUK_DDDPRINT 0 && /* args */ @@ -7537,7 +9445,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s #define DUK_DD(x) do { } while (0) /* omit */ #define DUK_DDD(x) do { } while (0) /* omit */ -#ifdef DUK_USE_VARIADIC_MACROS +#if defined(DUK_USE_VARIADIC_MACROS) #define DUK_DPRINT(...) #define DUK_DDPRINT(...) @@ -7557,7 +9465,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_s * Structs */ -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) struct duk_fixedbuffer { duk_uint8_t *buffer; duk_size_t length; @@ -7570,23 +9478,23 @@ struct duk_fixedbuffer { * Prototypes */ -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) DUK_INTERNAL_DECL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const char *format, va_list ap); #if 0 /*unused*/ DUK_INTERNAL_DECL duk_int_t duk_debug_snprintf(char *str, duk_size_t size, const char *format, ...); #endif DUK_INTERNAL_DECL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_uint8_t *fptr, duk_size_t fptr_size); -#ifdef DUK_USE_VARIADIC_MACROS -DUK_INTERNAL_DECL void duk_debug_log(duk_small_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...); +#if defined(DUK_USE_VARIADIC_MACROS) +DUK_INTERNAL_DECL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...); #else /* DUK_USE_VARIADIC_MACROS */ /* parameter passing, not thread safe */ #define DUK_DEBUG_STASH_SIZE 128 #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL_DECL char duk_debug_line_stash[DUK_DEBUG_STASH_SIZE]; +DUK_INTERNAL_DECL duk_int_t duk_debug_line_stash; DUK_INTERNAL_DECL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL_DECL duk_small_int_t duk_debug_level_stash; +DUK_INTERNAL_DECL duk_int_t duk_debug_level_stash; #endif DUK_INTERNAL_DECL void duk_debug_log(const char *fmt, ...); #endif /* DUK_USE_VARIADIC_MACROS */ @@ -7601,30 +9509,31 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #endif /* DUK_USE_DEBUG */ #endif /* DUK_DEBUG_H_INCLUDED */ -#line 1 "duk_error.h" +/* #include duk_error.h */ /* * Error handling macros, assertion macro, error codes. * - * There are three level of 'errors': + * There are three types of 'errors': * - * 1. Ordinary errors, relative to a thread, cause a longjmp, catchable. - * 2. Fatal errors, relative to a heap, cause fatal handler to be called. - * 3. Panic errors, unrelated to a heap and cause a process exit. + * 1. Ordinary errors relative to a thread, cause a longjmp, catchable. + * 2. Fatal errors relative to a heap, cause fatal handler to be called. + * 3. Fatal errors without context, cause the default (not heap specific) + * fatal handler to be called. * - * Panics are used by the default fatal error handler and by debug code - * such as assertions. By providing a proper fatal error handler, user - * code can avoid panics in non-debug builds. + * Fatal errors without context are used by debug code such as assertions. + * By providing a fatal error handler for a Duktape heap, user code can + * avoid fatal errors without context in non-debug builds. */ -#ifndef DUK_ERROR_H_INCLUDED +#if !defined(DUK_ERROR_H_INCLUDED) #define DUK_ERROR_H_INCLUDED /* * Error codes: defined in duktape.h * * Error codes are used as a shorthand to throw exceptions from inside - * the implementation. The appropriate Ecmascript object is constructed - * based on the code. Ecmascript code throws objects directly. The error + * the implementation. The appropriate ECMAScript object is constructed + * based on the code. ECMAScript code throws objects directly. The error * codes are defined in the public API header because they are also used * by calling code. */ @@ -7731,66 +9640,255 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #endif /* DUK_USE_VERBOSE_ERRORS */ /* - * Fatal error + * Fatal error without context * - * There are no fatal error macros at the moment. There are so few call - * sites that the fatal error handler is called directly. + * The macro is an expression to make it compatible with DUK_ASSERT_EXPR(). */ +#define DUK_FATAL_WITHOUT_CONTEXT(msg) \ + duk_default_fatal_handler(NULL, (msg)) + /* - * Panic error + * Error throwing helpers * - * Panic errors are not relative to either a heap or a thread, and cause - * DUK_PANIC() macro to be invoked. Unless a user provides DUK_USE_PANIC_HANDLER, - * DUK_PANIC() calls a helper which prints out the error and causes a process - * exit. + * The goal is to provide verbose and configurable error messages. Call + * sites should be clean in source code and compile to a small footprint. + * Small footprint is also useful for performance because small cold paths + * reduce code cache pressure. Adding macros here only makes sense if there + * are enough call sites to get concrete benefits. * - * The user can override the macro to provide custom handling. A macro is - * used to allow the user to have inline panic handling if desired (without - * causing a potentially risky function call). + * DUK_ERROR_xxx() macros are generic and can be used anywhere. * - * Panics are only used in debug code such as assertions, and by the default - * fatal error handler. + * DUK_DCERROR_xxx() macros can only be used in Duktape/C functions where + * the "return DUK_RET_xxx;" shorthand is available for low memory targets. + * The DUK_DCERROR_xxx() macros always either throw or perform a + * 'return DUK_RET_xxx' from the calling function. */ -#if defined(DUK_USE_PANIC_HANDLER) -/* already defined, good */ -#define DUK_PANIC(code,msg) DUK_USE_PANIC_HANDLER((code),(msg)) -#else -#define DUK_PANIC(code,msg) duk_default_panic_handler((code),(msg)) -#endif /* DUK_USE_PANIC_HANDLER */ +#if defined(DUK_USE_VERBOSE_ERRORS) +/* Verbose errors with key/value summaries (non-paranoid) or without key/value + * summaries (paranoid, for some security sensitive environments), the paranoid + * vs. non-paranoid distinction affects only a few specific errors. + */ +#if defined(DUK_USE_PARANOID_ERRORS) +#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \ + duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \ + } while (0) +#else /* DUK_USE_PARANOID_ERRORS */ +#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \ + duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx), (expectname)); \ + } while (0) +#endif /* DUK_USE_PARANOID_ERRORS */ + +#define DUK_ERROR_INTERNAL(thr) do { \ + duk_err_error_internal((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_DCERROR_INTERNAL(thr) do { \ + DUK_ERROR_INTERNAL((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_ALLOC_FAILED(thr) do { \ + duk_err_error_alloc_failed((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_ERROR_UNSUPPORTED(thr) do { \ + DUK_ERROR((thr), DUK_ERR_ERROR, DUK_STR_UNSUPPORTED); \ + } while (0) +#define DUK_DCERROR_UNSUPPORTED(thr) do { \ + DUK_ERROR_UNSUPPORTED((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_ERROR(thr,msg) do { \ + duk_err_error((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ + } while (0) +#define DUK_ERROR_RANGE_INDEX(thr,idx) do { \ + duk_err_range_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (idx)); \ + } while (0) +#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) do { \ + duk_err_range_push_beyond((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_ARGS(thr) do { \ + DUK_ERROR_RANGE((thr), DUK_STR_INVALID_ARGS); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) do { \ + DUK_ERROR_RANGE_INVALID_ARGS((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \ + DUK_ERROR_RANGE((thr), DUK_STR_INVALID_COUNT); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \ + DUK_ERROR_RANGE_INVALID_COUNT((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \ + DUK_ERROR_RANGE((thr), DUK_STR_INVALID_LENGTH); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) do { \ + DUK_ERROR_RANGE_INVALID_LENGTH((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_RANGE(thr,msg) do { \ + duk_err_range((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ + } while (0) +#define DUK_ERROR_EVAL(thr,msg) do { \ + DUK_ERROR((thr), DUK_ERR_EVAL_ERROR, (msg)); \ + } while (0) +#define DUK_ERROR_REFERENCE(thr,msg) do { \ + DUK_ERROR((thr), DUK_ERR_REFERENCE_ERROR, (msg)); \ + } while (0) +#define DUK_ERROR_SYNTAX(thr,msg) do { \ + DUK_ERROR((thr), DUK_ERR_SYNTAX_ERROR, (msg)); \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_ARGS(thr) do { \ + duk_err_type_invalid_args((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) do { \ + DUK_ERROR_TYPE_INVALID_ARGS((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_STATE(thr) do { \ + duk_err_type_invalid_state((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_STATE(thr) do { \ + DUK_ERROR_TYPE_INVALID_STATE((thr)); \ + return 0; \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \ + duk_err_type_invalid_trap_result((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \ + DUK_ERROR_TYPE((thr), DUK_STR_INVALID_TRAP_RESULT); \ + } while (0) +#define DUK_ERROR_TYPE(thr,msg) do { \ + DUK_ERROR((thr), DUK_ERR_TYPE_ERROR, (msg)); \ + } while (0) +#define DUK_ERROR_URI(thr,msg) do { \ + DUK_ERROR((thr), DUK_ERR_URI_ERROR, (msg)); \ + } while (0) +#else /* DUK_USE_VERBOSE_ERRORS */ +/* Non-verbose errors for low memory targets: no file, line, or message. */ + +#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,idx,expectname,lowmemstr) do { \ + duk_err_type((thr)); \ + } while (0) + +#define DUK_ERROR_INTERNAL(thr) do { \ + duk_err_error((thr)); \ + } while (0) +#define DUK_DCERROR_INTERNAL(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_ERROR; \ + } while (0) +#define DUK_ERROR_ALLOC_FAILED(thr) do { \ + duk_err_error((thr)); \ + } while (0) +#define DUK_ERROR_UNSUPPORTED(thr) do { \ + duk_err_error((thr)); \ + } while (0) +#define DUK_DCERROR_UNSUPPORTED(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_ERROR; \ + } while (0) +#define DUK_ERROR_ERROR(thr,msg) do { \ + duk_err_error((thr)); \ + } while (0) +#define DUK_ERROR_RANGE_INDEX(thr,idx) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_ERROR_RANGE_PUSH_BEYOND(thr) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_ARGS(thr) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_ARGS(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_RANGE_ERROR; \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_RANGE_ERROR; \ + } while (0) +#define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_DCERROR_RANGE_INVALID_LENGTH(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_RANGE_ERROR; \ + } while (0) +#define DUK_ERROR_RANGE(thr,msg) do { \ + duk_err_range((thr)); \ + } while (0) +#define DUK_ERROR_EVAL(thr,msg) do { \ + duk_err_eval((thr)); \ + } while (0) +#define DUK_ERROR_REFERENCE(thr,msg) do { \ + duk_err_reference((thr)); \ + } while (0) +#define DUK_ERROR_SYNTAX(thr,msg) do { \ + duk_err_syntax((thr)); \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_ARGS(thr) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_ARGS(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_TYPE_ERROR; \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_STATE(thr) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_STATE(thr) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_DCERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \ + DUK_UNREF((thr)); \ + return DUK_RET_TYPE_ERROR; \ + } while (0) +#define DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_ERROR_TYPE(thr,msg) do { \ + duk_err_type((thr)); \ + } while (0) +#define DUK_ERROR_URI(thr,msg) do { \ + duk_err_uri((thr)); \ + } while (0) +#endif /* DUK_USE_VERBOSE_ERRORS */ /* - * Assert macro: failure causes panic. + * Assert macro: failure causes a fatal error. + * + * NOTE: since the assert macro doesn't take a heap/context argument, there's + * no way to look up a heap/context specific fatal error handler which may have + * been given by the application. Instead, assertion failures always use the + * internal default fatal error handler; it can be replaced via duk_config.h + * and then applies to all Duktape heaps. */ #if defined(DUK_USE_ASSERTIONS) -/* the message should be a compile time constant without formatting (less risk); +/* The message should be a compile time constant without formatting (less risk); * we don't care about assertion text size because they're not used in production * builds. */ #define DUK_ASSERT(x) do { \ if (!(x)) { \ - DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \ - "assertion failed: " #x \ + DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \ " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \ } \ } while (0) -/* Assertion compatible inside a comma expression, evaluates to void. - * Currently not compatible with DUK_USE_PANIC_HANDLER() which may have - * a statement block. - */ -#if defined(DUK_USE_PANIC_HANDLER) -/* XXX: resolve macro definition issue or call through a helper function? */ -#define DUK_ASSERT_EXPR(x) ((void) 0) -#else +/* Assertion compatible inside a comma expression, evaluates to void. */ #define DUK_ASSERT_EXPR(x) \ - ((void) ((x) ? 0 : (DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \ - "assertion failed: " #x \ + ((void) ((x) ? 0 : (DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \ " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), 0))) -#endif #else /* DUK_USE_ASSERTIONS */ @@ -7835,6 +9933,22 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval) /* nop */ #endif +#define DUK_ASSERT_VS_SPACE(thr) \ + DUK_ASSERT(thr->valstack_top < thr->valstack_end) + +/* + * Helper to initialize a memory area (e.g. struct) with garbage when + * assertions enabled. + */ + +#if defined(DUK_USE_ASSERTIONS) +#define DUK_ASSERT_SET_GARBAGE(ptr,size) do { \ + duk_memset_unsafe((void *) (ptr), 0x5a, size); \ + } while (0) +#else +#define DUK_ASSERT_SET_GARBAGE(ptr,size) do {} while (0) +#endif + /* * Helper for valstack space * @@ -7855,126 +9969,6 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #define DUK_ASSERT_VALSTACK_SPACE(thr,n) /* no valstack space check */ #endif -/* - * Error throwing helpers - * - * The goal is to provide verbose and configurable error messages. Call - * sites should be clean in source code and compile to a small footprint. - * Small footprint is also useful for performance because small cold paths - * reduce code cache pressure. Adding macros here only makes sense if there - * are enough call sites to get concrete benefits. - */ - -#if defined(DUK_USE_VERBOSE_ERRORS) -/* Verbose errors with key/value summaries (non-paranoid) or without key/value - * summaries (paranoid, for some security sensitive environments), the paranoid - * vs. non-paranoid distinction affects only a few specific errors. - */ -#if defined(DUK_USE_PARANOID_ERRORS) -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,index,expectname,lowmemstr) do { \ - duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (index), (expectname)); \ - } while (0) -#else /* DUK_USE_PARANOID_ERRORS */ -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,index,expectname,lowmemstr) do { \ - duk_err_require_type_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (index), (expectname)); \ - } while (0) -#endif /* DUK_USE_PARANOID_ERRORS */ - -#define DUK_ERROR_UNIMPLEMENTED(thr,msg) do { \ - DUK_ERROR((thr), DUK_ERR_UNIMPLEMENTED_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_UNIMPLEMENTED_DEFMSG(thr) do { \ - duk_err_unimplemented_defmsg((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_ERROR_UNSUPPORTED(thr,msg) do { \ - DUK_ERROR((thr), DUK_ERR_UNSUPPORTED_ERROR, (msg)); \ - } while (0) -#if !defined(DUK_USE_BYTECODE_DUMP_SUPPORT) -#define DUK_ERROR_UNSUPPORTED_DEFMSG(thr) do { \ - duk_err_unsupported_defmsg((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#endif -#define DUK_ERROR_INTERNAL(thr,msg) do { \ - duk_err_internal((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -#define DUK_ERROR_INTERNAL_DEFMSG(thr) do { \ - duk_err_internal_defmsg((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \ - } while (0) -#define DUK_ERROR_ALLOC(thr,msg) do { \ - duk_err_alloc((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -#define DUK_ERROR_ALLOC_DEFMSG(thr) do { \ - DUK_ERROR_ALLOC((thr), DUK_STR_ALLOC_FAILED); \ - } while (0) -/* DUK_ERR_ASSERTION_ERROR: no macros needed */ -#define DUK_ERROR_API_INDEX(thr,index) do { \ - duk_err_api_index((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (index)); \ - } while (0) -#define DUK_ERROR_API(thr,msg) do { \ - duk_err_api((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -/* DUK_ERR_UNCAUGHT_ERROR: no macros needed */ -/* DUK_ERR_ERROR: no macros needed */ -/* DUK_ERR_EVAL: no macros needed */ -#define DUK_ERROR_RANGE(thr,msg) do { \ - duk_err_range((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO, (msg)); \ - } while (0) -/* DUK_ERR_REFERENCE_ERROR: no macros needed */ -#define DUK_ERROR_SYNTAX(thr,msg) do { \ - DUK_ERROR((thr), DUK_ERR_SYNTAX_ERROR, (msg)); \ - } while (0) -#define DUK_ERROR_TYPE(thr,msg) do { \ - DUK_ERROR((thr), DUK_ERR_TYPE_ERROR, (msg)); \ - } while (0) -/* DUK_ERR_URI_ERROR: no macros needed */ -#else /* DUK_USE_VERBOSE_ERRORS */ -/* Non-verbose errors for low memory targets: no file, line, or message. */ - -#define DUK_ERROR_REQUIRE_TYPE_INDEX(thr,index,expectname,lowmemstr) do { \ - duk_err_type((thr)); \ - } while (0) - -#define DUK_ERROR_UNIMPLEMENTED(thr,msg) do { \ - duk_err_unimplemented((thr)); \ - } while (0) -#define DUK_ERROR_UNIMPLEMENTED_DEFMSG(thr) do { \ - duk_err_unimplemented((thr)); \ - } while (0) -#define DUK_ERROR_UNSUPPORTED(thr,msg) do { \ - duk_err_unsupported((thr)); \ - } while (0) -#define DUK_ERROR_UNSUPPORTED_DEFMSG(thr) do { \ - duk_err_unsupported((thr)); \ - } while (0) -#define DUK_ERROR_INTERNAL(thr,msg) do { \ - duk_err_internal((thr)); \ - } while (0) -#define DUK_ERROR_INTERNAL_DEFMSG(thr) do { \ - duk_err_internal((thr)); \ - } while (0) -#define DUK_ERROR_ALLOC(thr,msg) do { \ - duk_err_alloc((thr)); \ - } while (0) -#define DUK_ERROR_ALLOC_DEFMSG(thr) do { \ - duk_err_alloc((thr)); \ - } while (0) -#define DUK_ERROR_API_INDEX(thr,index) do { \ - duk_err_api((thr)); \ - } while (0) -#define DUK_ERROR_API(thr,msg) do { \ - duk_err_api((thr)); \ - } while (0) -#define DUK_ERROR_RANGE(thr,msg) do { \ - duk_err_range((thr)); \ - } while (0) -#define DUK_ERROR_SYNTAX(thr,msg) do { \ - duk_err_syntax((thr)); \ - } while (0) -#define DUK_ERROR_TYPE(thr,msg) do { \ - duk_err_type((thr)); \ - } while (0) -#endif /* DUK_USE_VERBOSE_ERRORS */ - /* * Prototypes */ @@ -7994,8 +9988,11 @@ DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, d DUK_NORETURN(DUK_INTERNAL_DECL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc)); +#define DUK_AUGMENT_FLAG_NOBLAME_FILELINE (1U << 0) /* if set, don't blame C file/line for .fileName and .lineNumber */ +#define DUK_AUGMENT_FLAG_SKIP_ONE (1U << 1) /* if set, skip topmost activation in traceback construction */ + #if defined(DUK_USE_AUGMENT_ERROR_CREATE) -DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_bool_t noblame_fileline); +DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_uint_t flags); #endif #if defined(DUK_USE_AUGMENT_ERROR_THROW) DUK_INTERNAL_DECL void duk_err_augment_error_throw(duk_hthread *thr); @@ -8003,50 +10000,47 @@ DUK_INTERNAL_DECL void duk_err_augment_error_throw(duk_hthread *thr); #if defined(DUK_USE_VERBOSE_ERRORS) #if defined(DUK_USE_PARANOID_ERRORS) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index, const char *expect_name)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name)); #else -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index, const char *expect_name)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name)); #endif -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_api_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_api(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_unimplemented_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -#if !defined(DUK_USE_BYTECODE_DUMP_SUPPORT) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_unsupported_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -#endif -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_internal_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_alloc(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber)); #else /* DUK_VERBOSE_ERRORS */ +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_error(duk_hthread *thr)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_range(duk_hthread *thr)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_eval(duk_hthread *thr)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_reference(duk_hthread *thr)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_syntax(duk_hthread *thr)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_type(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_api(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_unimplemented(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_unsupported(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_internal(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_alloc(duk_hthread *thr)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_uri(duk_hthread *thr)); #endif /* DUK_VERBOSE_ERRORS */ DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_longjmp(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)); +DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, const char *msg)); -#if !defined(DUK_USE_PANIC_HANDLER) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_panic_handler(duk_errcode_t code, const char *msg)); +DUK_INTERNAL_DECL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val); +#if defined(DUK_USE_DEBUGGER_SUPPORT) +DUK_INTERNAL_DECL void duk_err_check_debugger_integration(duk_hthread *thr); #endif -DUK_INTERNAL_DECL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type); - DUK_INTERNAL_DECL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t err_code); #endif /* DUK_ERROR_H_INCLUDED */ -#line 1 "duk_unicode.h" +/* #include duk_unicode.h */ /* * Unicode helpers */ -#ifndef DUK_UNICODE_H_INCLUDED +#if !defined(DUK_UNICODE_H_INCLUDED) #define DUK_UNICODE_H_INCLUDED /* @@ -8209,25 +10203,34 @@ DUK_INTERNAL_DECL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, d #define DUK_ASC_TILDE 0x7e #define DUK_ASC_DEL 0x7f +/* + * Miscellaneous + */ + +/* Uppercase A is 0x41, lowercase a is 0x61; OR 0x20 to convert uppercase + * to lowercase. + */ +#define DUK_LOWERCASE_CHAR_ASCII(x) ((x) | 0x20) + /* * Unicode tables */ -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) /* * Automatically generated by extract_chars.py, do not edit! */ -extern const duk_uint8_t duk_unicode_ids_noa[791]; +extern const duk_uint8_t duk_unicode_ids_noa[1116]; #else /* * Automatically generated by extract_chars.py, do not edit! */ -extern const duk_uint8_t duk_unicode_ids_noabmp[611]; +extern const duk_uint8_t duk_unicode_ids_noabmp[625]; #endif -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) /* * Automatically generated by extract_chars.py, do not edit! */ @@ -8241,26 +10244,26 @@ extern const duk_uint8_t duk_unicode_ids_m_let_noa[42]; extern const duk_uint8_t duk_unicode_ids_m_let_noabmp[24]; #endif -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) /* * Automatically generated by extract_chars.py, do not edit! */ -extern const duk_uint8_t duk_unicode_idp_m_ids_noa[397]; +extern const duk_uint8_t duk_unicode_idp_m_ids_noa[576]; #else /* * Automatically generated by extract_chars.py, do not edit! */ -extern const duk_uint8_t duk_unicode_idp_m_ids_noabmp[348]; +extern const duk_uint8_t duk_unicode_idp_m_ids_noabmp[358]; #endif /* * Automatically generated by extract_caseconv.py, do not edit! */ -extern const duk_uint8_t duk_unicode_caseconv_uc[1288]; -extern const duk_uint8_t duk_unicode_caseconv_lc[616]; +extern const duk_uint8_t duk_unicode_caseconv_uc[1411]; +extern const duk_uint8_t duk_unicode_caseconv_lc[706]; #if defined(DUK_USE_REGEXP_CANON_WORKAROUND) /* @@ -8270,6 +10273,17 @@ extern const duk_uint8_t duk_unicode_caseconv_lc[616]; extern const duk_uint16_t duk_unicode_re_canon_lookup[65536]; #endif +#if defined(DUK_USE_REGEXP_CANON_BITMAP) +/* + * Automatically generated by extract_caseconv.py, do not edit! + */ + +#define DUK_CANON_BITMAP_BLKSIZE 32 +#define DUK_CANON_BITMAP_BLKSHIFT 5 +#define DUK_CANON_BITMAP_BLKMASK 31 +extern const duk_uint8_t duk_unicode_re_canon_bitmap[256]; +#endif + /* * Extern */ @@ -8299,29 +10313,32 @@ DUK_INTERNAL_DECL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, DUK_INTERNAL_DECL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end, duk_ucodepoint_t *out_cp); DUK_INTERNAL_DECL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, const duk_uint8_t **ptr, const duk_uint8_t *ptr_start, const duk_uint8_t *ptr_end); DUK_INTERNAL_DECL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *data, duk_size_t blen); +DUK_INTERNAL_DECL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_line_terminator(duk_codepoint_t cp); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp); DUK_INTERNAL_DECL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase); +#if defined(DUK_USE_REGEXP_SUPPORT) DUK_INTERNAL_DECL duk_codepoint_t duk_unicode_re_canonicalize_char(duk_hthread *thr, duk_codepoint_t cp); DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp); +#endif #endif /* DUK_UNICODE_H_INCLUDED */ -#line 1 "duk_json.h" +/* #include duk_json.h */ /* * Defines for JSON, especially duk_bi_json.c. */ -#ifndef DUK_JSON_H_INCLUDED +#if !defined(DUK_JSON_H_INCLUDED) #define DUK_JSON_H_INCLUDED /* Encoding/decoding flags */ -#define DUK_JSON_FLAG_ASCII_ONLY (1 << 0) /* escape any non-ASCII characters */ -#define DUK_JSON_FLAG_AVOID_KEY_QUOTES (1 << 1) /* avoid key quotes when key is an ASCII Identifier */ -#define DUK_JSON_FLAG_EXT_CUSTOM (1 << 2) /* extended types: custom encoding */ -#define DUK_JSON_FLAG_EXT_COMPATIBLE (1 << 3) /* extended types: compatible encoding */ +#define DUK_JSON_FLAG_ASCII_ONLY (1U << 0) /* escape any non-ASCII characters */ +#define DUK_JSON_FLAG_AVOID_KEY_QUOTES (1U << 1) /* avoid key quotes when key is an ASCII Identifier */ +#define DUK_JSON_FLAG_EXT_CUSTOM (1U << 2) /* extended types: custom encoding */ +#define DUK_JSON_FLAG_EXT_COMPATIBLE (1U << 3) /* extended types: compatible encoding */ /* How much stack to require on entry to object/array encode */ #define DUK_JSON_ENC_REQSTACK 32 @@ -8348,8 +10365,8 @@ typedef struct { duk_small_uint_t flag_ext_compatible; duk_small_uint_t flag_ext_custom_or_compatible; #endif - duk_int_t recursion_depth; - duk_int_t recursion_limit; + duk_uint_t recursion_depth; + duk_uint_t recursion_limit; duk_uint_t mask_for_undefined; /* type bit mask: types which certainly produce 'undefined' */ #if defined(DUK_USE_JX) || defined(DUK_USE_JC) duk_small_uint_t stridx_custom_undefined; @@ -8378,28 +10395,30 @@ typedef struct { } duk_json_dec_ctx; #endif /* DUK_JSON_H_INCLUDED */ -#line 1 "duk_js.h" +/* #include duk_js.h */ /* - * Ecmascript execution, support primitives. + * ECMAScript execution, support primitives. */ -#ifndef DUK_JS_H_INCLUDED +#if !defined(DUK_JS_H_INCLUDED) #define DUK_JS_H_INCLUDED -/* Flags for call handling. */ -#define DUK_CALL_FLAG_IGNORE_RECLIMIT (1 << 0) /* duk_handle_call_xxx: call ignores C recursion limit (for errhandler calls) */ -#define DUK_CALL_FLAG_CONSTRUCTOR_CALL (1 << 1) /* duk_handle_call_xxx: constructor call (i.e. called as 'new Foo()') */ -#define DUK_CALL_FLAG_IS_RESUME (1 << 2) /* duk_handle_ecma_call_setup: setup for a resume() */ -#define DUK_CALL_FLAG_IS_TAILCALL (1 << 3) /* duk_handle_ecma_call_setup: setup for a tail call */ -#define DUK_CALL_FLAG_DIRECT_EVAL (1 << 4) /* call is a direct eval call */ +/* Flags for call handling. Lowest flags must match bytecode DUK_BC_CALL_FLAG_xxx 1:1. */ +#define DUK_CALL_FLAG_TAILCALL (1U << 0) /* setup for a tail call */ +#define DUK_CALL_FLAG_CONSTRUCT (1U << 1) /* constructor call (i.e. called as 'new Foo()') */ +#define DUK_CALL_FLAG_CALLED_AS_EVAL (1U << 2) /* call was made using the identifier 'eval' */ +#define DUK_CALL_FLAG_ALLOW_ECMATOECMA (1U << 3) /* ecma-to-ecma call with executor reuse is possible */ +#define DUK_CALL_FLAG_DIRECT_EVAL (1U << 4) /* call is a direct eval call */ +#define DUK_CALL_FLAG_CONSTRUCT_PROXY (1U << 5) /* handled via 'construct' proxy trap, check return value invariant(s) */ +#define DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED (1U << 6) /* prototype of 'default instance' updated, temporary flag in call handling */ /* Flags for duk_js_equals_helper(). */ -#define DUK_EQUALS_FLAG_SAMEVALUE (1 << 0) /* use SameValue instead of non-strict equality */ -#define DUK_EQUALS_FLAG_STRICT (1 << 1) /* use strict equality instead of non-strict equality */ +#define DUK_EQUALS_FLAG_SAMEVALUE (1U << 0) /* use SameValue instead of non-strict equality */ +#define DUK_EQUALS_FLAG_STRICT (1U << 1) /* use strict equality instead of non-strict equality */ /* Flags for duk_js_compare_helper(). */ -#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST (1 << 0) /* eval left argument first */ -#define DUK_COMPARE_FLAG_NEGATE (1 << 1) /* negate result */ +#define DUK_COMPARE_FLAG_NEGATE (1U << 0) /* negate result */ +#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST (1U << 1) /* eval left argument first */ /* conversions, coercions, comparison, etc */ DUK_INTERNAL_DECL duk_bool_t duk_js_toboolean(duk_tval *tv); @@ -8409,18 +10428,30 @@ DUK_INTERNAL_DECL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t *str, duk_uint32_t blen, duk_uarridx_t *out_idx); -DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h); -DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags); +DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen); +#if !defined(DUK_USE_HSTRING_ARRIDX) +DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h); +DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h); +#endif +DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, const duk_uint8_t *buf2, duk_size_t len1, duk_size_t len2); DUK_INTERNAL_DECL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2); #if 0 /* unused */ DUK_INTERNAL_DECL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2); #endif -DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags); +DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); +#if defined(DUK_USE_SYMBOL_BUILTIN) +DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); +#endif DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); -DUK_INTERNAL_DECL duk_hstring *duk_js_typeof(duk_hthread *thr, duk_tval *tv_x); +DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x); +DUK_INTERNAL_DECL duk_bool_t duk_js_isarray_hobject(duk_hobject *h); +DUK_INTERNAL_DECL duk_bool_t duk_js_isarray(duk_tval *tv); + +/* arithmetic */ +DUK_INTERNAL_DECL double duk_js_arith_pow(double x, double y); +DUK_INTERNAL_DECL double duk_js_arith_mod(double x, double y); #define duk_js_equals(thr,tv_x,tv_y) \ duk_js_equals_helper((thr), (tv_x), (tv_y), 0) @@ -8457,56 +10488,59 @@ DUK_INTERNAL_DECL void duk_js_putvar_activation(duk_hthread *thr, duk_activation DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name); #endif DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name); -DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_small_int_t prop_flags, duk_bool_t is_func_decl); +DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_small_uint_t prop_flags, duk_bool_t is_func_decl); DUK_INTERNAL_DECL void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, duk_activation *act); -DUK_INTERNAL_DECL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env, duk_hobject *func, duk_size_t regbase); -DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t idx_bottom); -DUK_INTERNAL_DECL -void duk_js_push_closure(duk_hthread *thr, - duk_hcompiledfunction *fun_temp, - duk_hobject *outer_var_env, - duk_hobject *outer_lex_env, - duk_bool_t add_auto_proto); +DUK_INTERNAL_DECL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env); +DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t bottom_byteoff); +DUK_INTERNAL_DECL void duk_js_push_closure(duk_hthread *thr, + duk_hcompfunc *fun_temp, + duk_hobject *outer_var_env, + duk_hobject *outer_lex_env, + duk_bool_t add_auto_proto); /* call handling */ -DUK_INTERNAL_DECL duk_int_t duk_handle_call_protected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); -DUK_INTERNAL_DECL void duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); -DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, duk_idx_t num_stack_args, duk_idx_t num_stack_res); -DUK_INTERNAL_DECL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags); +DUK_INTERNAL_DECL void duk_native_stack_check(duk_hthread *thr); +DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags); +DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags); +DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t num_stack_args, duk_idx_t num_stack_res); +DUK_INTERNAL_DECL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant); +#if defined(DUK_USE_VERBOSE_ERRORS) +DUK_INTERNAL_DECL void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_base, duk_tval *tv_key); +#endif /* bytecode execution */ DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr); #endif /* DUK_JS_H_INCLUDED */ -#line 1 "duk_numconv.h" -#ifndef DUK_NUMCONV_H_INCLUDED -#define DUK_NUMCONV_H_INCLUDED - +/* #include duk_numconv.h */ /* * Number-to-string conversion. The semantics of these is very tightly - * bound with the Ecmascript semantics required for call sites. + * bound with the ECMAScript semantics required for call sites. */ +#if !defined(DUK_NUMCONV_H_INCLUDED) +#define DUK_NUMCONV_H_INCLUDED + /* Output a specified number of digits instead of using the shortest * form. Used for toPrecision() and toFixed(). */ -#define DUK_N2S_FLAG_FIXED_FORMAT (1 << 0) +#define DUK_N2S_FLAG_FIXED_FORMAT (1U << 0) /* Force exponential format. Used for toExponential(). */ -#define DUK_N2S_FLAG_FORCE_EXP (1 << 1) +#define DUK_N2S_FLAG_FORCE_EXP (1U << 1) /* If number would need zero padding (for whole number part), use * exponential format instead. E.g. if input number is 12300, 3 * digits are generated ("123"), output "1.23e+4" instead of "12300". * Used for toPrecision(). */ -#define DUK_N2S_FLAG_NO_ZERO_PAD (1 << 2) +#define DUK_N2S_FLAG_NO_ZERO_PAD (1U << 2) /* Digit count indicates number of fractions (i.e. an absolute * digit index instead of a relative one). Used together with * DUK_N2S_FLAG_FIXED_FORMAT for toFixed(). */ -#define DUK_N2S_FLAG_FRACTION_DIGITS (1 << 3) +#define DUK_N2S_FLAG_FRACTION_DIGITS (1U << 3) /* * String-to-number conversion @@ -8514,157 +10548,175 @@ DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr); /* Maximum exponent value when parsing numbers. This is not strictly * compliant as there should be no upper limit, but as we parse the - * exponent without a bigint, impose some limit. + * exponent without a bigint, impose some limit. The limit should be + * small enough that multiplying it (or limit-1 to be precise) won't + * overflow signed 32-bit integer range. Exponent is only parsed with + * radix 10, but with maximum radix (36) a safe limit is: + * (10000000*36).toString(16) -> '15752a00' */ -#define DUK_S2N_MAX_EXPONENT 1000000000 +#define DUK_S2N_MAX_EXPONENT 10000000L /* Trim white space (= allow leading and trailing whitespace) */ -#define DUK_S2N_FLAG_TRIM_WHITE (1 << 0) +#define DUK_S2N_FLAG_TRIM_WHITE (1U << 0) /* Allow exponent */ -#define DUK_S2N_FLAG_ALLOW_EXP (1 << 1) +#define DUK_S2N_FLAG_ALLOW_EXP (1U << 1) /* Allow trailing garbage (e.g. treat "123foo" as "123) */ -#define DUK_S2N_FLAG_ALLOW_GARBAGE (1 << 2) +#define DUK_S2N_FLAG_ALLOW_GARBAGE (1U << 2) /* Allow leading plus sign */ -#define DUK_S2N_FLAG_ALLOW_PLUS (1 << 3) +#define DUK_S2N_FLAG_ALLOW_PLUS (1U << 3) /* Allow leading minus sign */ -#define DUK_S2N_FLAG_ALLOW_MINUS (1 << 4) +#define DUK_S2N_FLAG_ALLOW_MINUS (1U << 4) /* Allow 'Infinity' */ -#define DUK_S2N_FLAG_ALLOW_INF (1 << 5) +#define DUK_S2N_FLAG_ALLOW_INF (1U << 5) /* Allow fraction part */ -#define DUK_S2N_FLAG_ALLOW_FRAC (1 << 6) +#define DUK_S2N_FLAG_ALLOW_FRAC (1U << 6) /* Allow naked fraction (e.g. ".123") */ -#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC (1 << 7) +#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC (1U << 7) /* Allow empty fraction (e.g. "123.") */ -#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC (1 << 8) +#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC (1U << 8) /* Allow empty string to be interpreted as 0 */ -#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO (1 << 9) +#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO (1U << 9) /* Allow leading zeroes (e.g. "0123" -> "123") */ -#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO (1 << 10) +#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO (1U << 10) /* Allow automatic detection of hex base ("0x" or "0X" prefix), * overrides radix argument and forces integer mode. */ -#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT (1 << 11) +#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT (1U << 11) -/* Allow automatic detection of octal base, overrides radix - * argument and forces integer mode. +/* Allow automatic detection of legacy octal base ("0n"), + * overrides radix argument and forces integer mode. */ -#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT (1 << 12) +#define DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT (1U << 12) + +/* Allow automatic detection of ES2015 octal base ("0o123"), + * overrides radix argument and forces integer mode. + */ +#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT (1U << 13) + +/* Allow automatic detection of ES2015 binary base ("0b10001"), + * overrides radix argument and forces integer mode. + */ +#define DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT (1U << 14) /* * Prototypes */ -DUK_INTERNAL_DECL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk_small_uint_t flags); +DUK_INTERNAL_DECL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags); +DUK_INTERNAL_DECL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags); #endif /* DUK_NUMCONV_H_INCLUDED */ -#line 1 "duk_bi_protos.h" +/* #include duk_bi_protos.h */ /* * Prototypes for built-in functions not automatically covered by the * header declarations emitted by genbuiltins.py. */ -#ifndef DUK_BUILTIN_PROTOS_H_INCLUDED +#if !defined(DUK_BUILTIN_PROTOS_H_INCLUDED) #define DUK_BUILTIN_PROTOS_H_INCLUDED -/* Buffer size needed for duk_bi_date_format_timeval(). +/* Buffer size needed for ISO 8601 formatting. * Accurate value is 32 + 1 for NUL termination: * >>> len('+123456-01-23T12:34:56.123+12:34') * 32 * Include additional space to be safe. */ -#define DUK_BI_DATE_ISO8601_BUFSIZE 48 - -/* Maximum length of CommonJS module identifier to resolve. Length includes - * both current module ID, requested (possibly relative) module ID, and a - * slash in between. - */ -#define DUK_BI_COMMONJS_MODULE_ID_LIMIT 256 +#define DUK_BI_DATE_ISO8601_BUFSIZE 40 /* Helpers exposed for internal use */ DUK_INTERNAL_DECL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dparts, duk_small_uint_t flags); -DUK_INTERNAL_DECL void duk_bi_date_format_timeval(duk_double_t timeval, duk_uint8_t *out_buf); DUK_INTERNAL_DECL duk_bool_t duk_bi_date_is_leap_year(duk_int_t year); DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_valid_range(duk_double_t x); DUK_INTERNAL_DECL duk_bool_t duk_bi_date_year_in_valid_range(duk_double_t year); DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x); /* Built-in providers */ #if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx); +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(void); #endif #if defined(DUK_USE_DATE_NOW_TIME) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(duk_context *ctx); +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(void); #endif #if defined(DUK_USE_DATE_NOW_WINDOWS) -DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(duk_context *ctx); +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(void); #endif -#if defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME) +#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows_subms(void); +#endif +#if defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d); #endif #if defined(DUK_USE_DATE_TZO_WINDOWS) DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d); #endif +#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) +DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d); +#endif #if defined(DUK_USE_DATE_PRS_STRPTIME) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, const char *str); +DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str); #endif #if defined(DUK_USE_DATE_PRS_GETDATE) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const char *str); +DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str); #endif #if defined(DUK_USE_DATE_FMT_STRFTIME) -DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags); +DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags); +#endif + +#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void); +#endif +#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) +DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void); #endif DUK_INTERNAL_DECL -void duk_bi_json_parse_helper(duk_context *ctx, +void duk_bi_json_parse_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_reviver, duk_small_uint_t flags); DUK_INTERNAL_DECL -void duk_bi_json_stringify_helper(duk_context *ctx, +void duk_bi_json_stringify_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_replacer, duk_idx_t idx_space, duk_small_uint_t flags); +DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr); + +#if defined(DUK_USE_ES6_PROXY) +DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags); +#endif + #endif /* DUK_BUILTIN_PROTOS_H_INCLUDED */ -#line 1 "duk_selftest.h" +/* #include duk_selftest.h */ /* * Selftest code */ -#ifndef DUK_SELFTEST_H_INCLUDED +#if !defined(DUK_SELFTEST_H_INCLUDED) #define DUK_SELFTEST_H_INCLUDED #if defined(DUK_USE_SELF_TESTS) -DUK_INTERNAL_DECL void duk_selftest_run_tests(void); +DUK_INTERNAL_DECL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_func, + duk_realloc_function realloc_func, + duk_free_function free_func, + void *udata); #endif #endif /* DUK_SELFTEST_H_INCLUDED */ -#line 78 "duk_internal.h" #endif /* DUK_INTERNAL_H_INCLUDED */ -#line 1 "duk_replacements.c" -/* - * Replacements for missing platform functions. - * - * Unlike the originals, fpclassify() and signbit() replacements don't - * work on any floating point types, only doubles. The C typing here - * mimics the standard prototypes. - */ - -/* include removed: duk_internal.h */ #if defined(DUK_USE_COMPUTED_NAN) DUK_INTERNAL double duk_computed_nan; @@ -8738,130 +10790,13 @@ DUK_INTERNAL int duk_repl_isinf(double x) { return (c == DUK_FP_INFINITE); } #endif -#line 1 "duk_strings.c" -/* - * Shared error message strings - * - * To minimize code footprint, try to share error messages inside Duktape - * code. Modern compilers will do this automatically anyway, this is mostly - * for older compilers. - */ - -/* include removed: duk_internal.h */ - -/* Mostly API and built-in method related */ -DUK_INTERNAL const char *duk_str_internal_error = "internal error"; -DUK_INTERNAL const char *duk_str_invalid_count = "invalid count"; -DUK_INTERNAL const char *duk_str_invalid_call_args = "invalid call args"; -DUK_INTERNAL const char *duk_str_not_constructable = "not constructable"; -DUK_INTERNAL const char *duk_str_not_callable = "not callable"; -DUK_INTERNAL const char *duk_str_not_extensible = "not extensible"; -DUK_INTERNAL const char *duk_str_not_writable = "not writable"; -DUK_INTERNAL const char *duk_str_not_configurable = "not configurable"; - -DUK_INTERNAL const char *duk_str_invalid_context = "invalid context"; -DUK_INTERNAL const char *duk_str_push_beyond_alloc_stack = "attempt to push beyond currently allocated stack"; -DUK_INTERNAL const char *duk_str_not_buffer = "not buffer"; /* still in use with verbose messages */ -DUK_INTERNAL const char *duk_str_unexpected_type = "unexpected type"; -DUK_INTERNAL const char *duk_str_defaultvalue_coerce_failed = "[[DefaultValue]] coerce failed"; -DUK_INTERNAL const char *duk_str_number_outside_range = "number outside range"; -DUK_INTERNAL const char *duk_str_not_object_coercible = "not object coercible"; -DUK_INTERNAL const char *duk_str_string_too_long = "string too long"; -DUK_INTERNAL const char *duk_str_buffer_too_long = "buffer too long"; -DUK_INTERNAL const char *duk_str_sprintf_too_long = "sprintf message too long"; -DUK_INTERNAL const char *duk_str_alloc_failed = "alloc failed"; -DUK_INTERNAL const char *duk_str_pop_too_many = "attempt to pop too many entries"; -DUK_INTERNAL const char *duk_str_wrong_buffer_type = "wrong buffer type"; -DUK_INTERNAL const char *duk_str_encode_failed = "encode failed"; -DUK_INTERNAL const char *duk_str_decode_failed = "decode failed"; -DUK_INTERNAL const char *duk_str_no_sourcecode = "no sourcecode"; -DUK_INTERNAL const char *duk_str_concat_result_too_long = "concat result too long"; -DUK_INTERNAL const char *duk_str_unimplemented = "unimplemented"; -DUK_INTERNAL const char *duk_str_unsupported = "unsupported"; -DUK_INTERNAL const char *duk_str_array_length_over_2g = "array length over 2G"; - -/* JSON */ -DUK_INTERNAL const char *duk_str_fmt_ptr = "%p"; -DUK_INTERNAL const char *duk_str_fmt_invalid_json = "invalid json (at offset %ld)"; -DUK_INTERNAL const char *duk_str_jsondec_reclimit = "json decode recursion limit"; -DUK_INTERNAL const char *duk_str_jsonenc_reclimit = "json encode recursion limit"; -DUK_INTERNAL const char *duk_str_cyclic_input = "cyclic input"; - -/* Object property access */ -DUK_INTERNAL const char *duk_str_proxy_revoked = "proxy revoked"; -DUK_INTERNAL const char *duk_str_invalid_base = "invalid base value"; -DUK_INTERNAL const char *duk_str_strict_caller_read = "attempt to read strict 'caller'"; -DUK_INTERNAL const char *duk_str_proxy_rejected = "proxy rejected"; -DUK_INTERNAL const char *duk_str_invalid_array_length = "invalid array length"; -DUK_INTERNAL const char *duk_str_array_length_write_failed = "array length write failed"; -DUK_INTERNAL const char *duk_str_array_length_not_writable = "array length non-writable"; -DUK_INTERNAL const char *duk_str_setter_undefined = "setter undefined"; -DUK_INTERNAL const char *duk_str_redefine_virt_prop = "attempt to redefine virtual property"; -DUK_INTERNAL const char *duk_str_invalid_descriptor = "invalid descriptor"; -DUK_INTERNAL const char *duk_str_property_is_virtual = "property is virtual"; - -/* Compiler */ -DUK_INTERNAL const char *duk_str_parse_error = "parse error"; -DUK_INTERNAL const char *duk_str_duplicate_label = "duplicate label"; -DUK_INTERNAL const char *duk_str_invalid_label = "invalid label"; -DUK_INTERNAL const char *duk_str_invalid_array_literal = "invalid array literal"; -DUK_INTERNAL const char *duk_str_invalid_object_literal = "invalid object literal"; -DUK_INTERNAL const char *duk_str_invalid_var_declaration = "invalid variable declaration"; -DUK_INTERNAL const char *duk_str_cannot_delete_identifier = "cannot delete identifier"; -DUK_INTERNAL const char *duk_str_invalid_expression = "invalid expression"; -DUK_INTERNAL const char *duk_str_invalid_lvalue = "invalid lvalue"; -DUK_INTERNAL const char *duk_str_expected_identifier = "expected identifier"; -DUK_INTERNAL const char *duk_str_empty_expr_not_allowed = "empty expression not allowed"; -DUK_INTERNAL const char *duk_str_invalid_for = "invalid for statement"; -DUK_INTERNAL const char *duk_str_invalid_switch = "invalid switch statement"; -DUK_INTERNAL const char *duk_str_invalid_break_cont_label = "invalid break/continue label"; -DUK_INTERNAL const char *duk_str_invalid_return = "invalid return"; -DUK_INTERNAL const char *duk_str_invalid_try = "invalid try"; -DUK_INTERNAL const char *duk_str_invalid_throw = "invalid throw"; -DUK_INTERNAL const char *duk_str_with_in_strict_mode = "with in strict mode"; -DUK_INTERNAL const char *duk_str_func_stmt_not_allowed = "function statement not allowed"; -DUK_INTERNAL const char *duk_str_unterminated_stmt = "unterminated statement"; -DUK_INTERNAL const char *duk_str_invalid_arg_name = "invalid argument name"; -DUK_INTERNAL const char *duk_str_invalid_func_name = "invalid function name"; -DUK_INTERNAL const char *duk_str_invalid_getset_name = "invalid getter/setter name"; -DUK_INTERNAL const char *duk_str_func_name_required = "function name required"; - -/* Regexp */ -DUK_INTERNAL const char *duk_str_invalid_quantifier_no_atom = "quantifier without preceding atom"; -DUK_INTERNAL const char *duk_str_invalid_quantifier_values = "quantifier values invalid (qmin > qmax)"; -DUK_INTERNAL const char *duk_str_quantifier_too_many_copies = "quantifier expansion requires too many atom copies"; -DUK_INTERNAL const char *duk_str_unexpected_closing_paren = "unexpected closing parenthesis"; -DUK_INTERNAL const char *duk_str_unexpected_end_of_pattern = "unexpected end of pattern"; -DUK_INTERNAL const char *duk_str_unexpected_regexp_token = "unexpected token in regexp"; -DUK_INTERNAL const char *duk_str_invalid_regexp_flags = "invalid regexp flags"; -DUK_INTERNAL const char *duk_str_invalid_backrefs = "invalid backreference(s)"; - -/* Limits */ -DUK_INTERNAL const char *duk_str_valstack_limit = "valstack limit"; -DUK_INTERNAL const char *duk_str_callstack_limit = "callstack limit"; -DUK_INTERNAL const char *duk_str_catchstack_limit = "catchstack limit"; -DUK_INTERNAL const char *duk_str_prototype_chain_limit = "prototype chain limit"; -DUK_INTERNAL const char *duk_str_bound_chain_limit = "function call bound chain limit"; -DUK_INTERNAL const char *duk_str_c_callstack_limit = "C call stack depth limit"; -DUK_INTERNAL const char *duk_str_compiler_recursion_limit = "compiler recursion limit"; -DUK_INTERNAL const char *duk_str_bytecode_limit = "bytecode limit"; -DUK_INTERNAL const char *duk_str_reg_limit = "register limit"; -DUK_INTERNAL const char *duk_str_temp_limit = "temp limit"; -DUK_INTERNAL const char *duk_str_const_limit = "const limit"; -DUK_INTERNAL const char *duk_str_func_limit = "function limit"; -DUK_INTERNAL const char *duk_str_regexp_compiler_recursion_limit = "regexp compiler recursion limit"; -DUK_INTERNAL const char *duk_str_regexp_executor_recursion_limit = "regexp executor recursion limit"; -DUK_INTERNAL const char *duk_str_regexp_executor_step_limit = "regexp step limit"; - -/* Misc */ -#line 1 "duk_debug_macros.c" /* * Debugging macro calls. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -#ifdef DUK_USE_DEBUG +#if defined(DUK_USE_DEBUG) /* * Debugging enabled @@ -8871,91 +10806,34 @@ DUK_INTERNAL const char *duk_str_regexp_executor_step_limit = "regexp step limit #include #include +#if !defined(DUK_USE_DEBUG_WRITE) +#error debugging enabled (DUK_USE_DEBUG) but DUK_USE_DEBUG_WRITE not defined +#endif + #define DUK__DEBUG_BUFSIZE DUK_USE_DEBUG_BUFSIZE -DUK_LOCAL char duk__debug_buf[DUK__DEBUG_BUFSIZE]; -DUK_LOCAL const char *duk__get_level_string(duk_small_int_t level) { - switch ((int) level) { - case DUK_LEVEL_DEBUG: - return "D"; - case DUK_LEVEL_DDEBUG: - return "DD"; - case DUK_LEVEL_DDDEBUG: - return "DDD"; - } - return "???"; -} +#if defined(DUK_USE_VARIADIC_MACROS) -#ifdef DUK_USE_DPRINT_COLORS - -/* http://en.wikipedia.org/wiki/ANSI_escape_code */ -#define DUK__TERM_REVERSE "\x1b[7m" -#define DUK__TERM_BRIGHT "\x1b[1m" -#define DUK__TERM_RESET "\x1b[0m" -#define DUK__TERM_BLUE "\x1b[34m" -#define DUK__TERM_RED "\x1b[31m" - -DUK_LOCAL const char *duk__get_term_1(duk_small_int_t level) { - DUK_UNREF(level); - return (const char *) DUK__TERM_RED; -} - -DUK_LOCAL const char *duk__get_term_2(duk_small_int_t level) { - switch ((int) level) { - case DUK_LEVEL_DEBUG: - return (const char *) (DUK__TERM_RESET DUK__TERM_BRIGHT); - case DUK_LEVEL_DDEBUG: - return (const char *) (DUK__TERM_RESET); - case DUK_LEVEL_DDDEBUG: - return (const char *) (DUK__TERM_RESET DUK__TERM_BLUE); - } - return (const char *) DUK__TERM_RESET; -} - -DUK_LOCAL const char *duk__get_term_3(duk_small_int_t level) { - DUK_UNREF(level); - return (const char *) DUK__TERM_RESET; -} - -#else - -DUK_LOCAL const char *duk__get_term_1(duk_small_int_t level) { - DUK_UNREF(level); - return (const char *) ""; -} - -DUK_LOCAL const char *duk__get_term_2(duk_small_int_t level) { - DUK_UNREF(level); - return (const char *) ""; -} - -DUK_LOCAL const char *duk__get_term_3(duk_small_int_t level) { - DUK_UNREF(level); - return (const char *) ""; -} - -#endif /* DUK_USE_DPRINT_COLORS */ - -#ifdef DUK_USE_VARIADIC_MACROS - -DUK_INTERNAL void duk_debug_log(duk_small_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...) { +DUK_INTERNAL void duk_debug_log(duk_int_t level, const char *file, duk_int_t line, const char *func, const char *fmt, ...) { va_list ap; + long arg_level; + const char *arg_file; + long arg_line; + const char *arg_func; + const char *arg_msg; + char buf[DUK__DEBUG_BUFSIZE]; va_start(ap, fmt); - DUK_MEMZERO((void *) duk__debug_buf, (size_t) DUK__DEBUG_BUFSIZE); - duk_debug_vsnprintf(duk__debug_buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); + duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE); + duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); - DUK_FPRINTF(DUK_STDERR, "%s[%s] %s:%ld (%s):%s %s%s\n", - (const char *) duk__get_term_1(level), - (const char *) duk__get_level_string(level), - (const char *) file, - (long) line, - (const char *) func, - (const char *) duk__get_term_2(level), - (const char *) duk__debug_buf, - (const char *) duk__get_term_3(level)); - DUK_FFLUSH(DUK_STDERR); + arg_level = (long) level; + arg_file = (const char *) file; + arg_line = (long) line; + arg_func = (const char *) func; + arg_msg = (const char *) buf; + DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg); va_end(ap); } @@ -8963,29 +10841,30 @@ DUK_INTERNAL void duk_debug_log(duk_small_int_t level, const char *file, duk_int #else /* DUK_USE_VARIADIC_MACROS */ DUK_INTERNAL char duk_debug_file_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL char duk_debug_line_stash[DUK_DEBUG_STASH_SIZE]; +DUK_INTERNAL duk_int_t duk_debug_line_stash; DUK_INTERNAL char duk_debug_func_stash[DUK_DEBUG_STASH_SIZE]; -DUK_INTERNAL duk_small_int_t duk_debug_level_stash; +DUK_INTERNAL duk_int_t duk_debug_level_stash; DUK_INTERNAL void duk_debug_log(const char *fmt, ...) { va_list ap; - duk_small_int_t level = duk_debug_level_stash; + long arg_level; + const char *arg_file; + long arg_line; + const char *arg_func; + const char *arg_msg; + char buf[DUK__DEBUG_BUFSIZE]; va_start(ap, fmt); - DUK_MEMZERO((void *) duk__debug_buf, (size_t) DUK__DEBUG_BUFSIZE); - duk_debug_vsnprintf(duk__debug_buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); + duk_memzero((void *) buf, (size_t) DUK__DEBUG_BUFSIZE); + duk_debug_vsnprintf(buf, DUK__DEBUG_BUFSIZE - 1, fmt, ap); - DUK_FPRINTF(DUK_STDERR, "%s[%s] %s:%s (%s):%s %s%s\n", - (const char *) duk__get_term_1(level), - (const char *) duk__get_level_string(duk_debug_level_stash), - (const char *) duk_debug_file_stash, - (const char *) duk_debug_line_stash, - (const char *) duk_debug_func_stash, - (const char *) duk__get_term_2(level), - (const char *) duk__debug_buf, - (const char *) duk__get_term_3(level)); - DUK_FFLUSH(DUK_STDERR); + arg_level = (long) duk_debug_level_stash; + arg_file = (const char *) duk_debug_file_stash; + arg_line = (long) duk_debug_line_stash; + arg_func = (const char *) duk_debug_func_stash; + arg_msg = (const char *) buf; + DUK_USE_DEBUG_WRITE(arg_level, arg_file, arg_line, arg_func, arg_msg); va_end(ap); } @@ -8999,75 +10878,81 @@ DUK_INTERNAL void duk_debug_log(const char *fmt, ...) { */ #endif /* DUK_USE_DEBUG */ -#line 1 "duk_builtins.c" + +/* automatic undefs */ +#undef DUK__DEBUG_BUFSIZE /* * Automatically generated by genbuiltins.py, do not edit! */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +#if defined(DUK_USE_ASSERTIONS) +#define DUK__REFCINIT(refc) 0 /*h_assert_refcount*/, (refc) /*actual*/ +#else +#define DUK__REFCINIT(refc) (refc) /*actual*/ +#endif #if defined(DUK_USE_ROM_STRINGS) -#error ROM support not enabled, rerun make_dist.py with --rom-support +#error ROM support not enabled, rerun configure.py with --rom-support #else /* DUK_USE_ROM_STRINGS */ -DUK_INTERNAL const duk_uint8_t duk_strings_data[1049] = { -79,104,209,144,168,105,6,78,182,139,90,122,8,154,140,35,103,35,117,193,73, -5,52,116,180,104,166,135,52,189,4,98,12,27,178,156,80,211,31,161,115,150, -64,52,221,109,24,18,68,157,24,38,67,118,36,55,73,119,151,164,140,93,18,117, -128,153,201,228,201,205,2,250,8,196,24,232,104,82,146,40,232,193,48,118, -168,37,147,212,54,127,113,208,70,32,194,187,68,54,127,113,208,70,32,196, -123,68,54,127,113,209,44,12,121,7,208,70,32,194,186,134,207,236,126,219, -160,140,65,133,246,136,108,254,199,237,186,8,196,24,87,80,217,253,159,217, -116,17,136,48,190,209,13,159,217,253,151,65,24,131,12,233,86,224,79,236, -254,203,160,140,65,134,116,171,112,39,246,223,105,208,70,32,193,140,183,4, -11,55,92,20,244,141,169,186,50,11,164,109,77,208,208,165,36,79,215,185,13, -153,34,110,204,241,32,6,66,84,11,112,200,84,52,157,124,92,242,70,120,45,64, -186,17,22,138,38,0,172,140,19,154,84,26,145,0,86,69,17,180,97,34,0,172,132, -75,144,215,77,221,91,132,5,147,178,156,80,211,30,160,93,9,215,21,115,119, -169,49,75,211,138,26,101,205,222,68,157,47,78,40,105,151,55,120,204,156, -189,56,161,166,52,157,72,136,138,65,154,232,147,162,4,136,150,81,115,66, -208,210,37,96,148,250,134,140,151,39,212,125,255,221,125,73,80,209,146,233, -124,93,55,79,15,34,196,230,202,113,160,166,232,157,132,148,128,98,28,46, -114,200,6,153,180,96,73,19,74,113,67,76,103,5,36,20,211,70,140,133,67,72, -49,245,160,235,81,212,52,168,106,39,132,253,111,80,210,161,168,158,5,245, -191,96,31,172,15,208,23,226,190,131,232,62,131,232,11,251,127,93,245,223, -93,251,172,234,27,80,45,3,250,14,140,19,34,65,19,81,132,108,228,97,1,107, -33,12,32,45,100,136,206,9,12,196,155,134,69,146,100,235,226,231,146,51,194, -72,218,48,145,4,200,119,89,189,81,49,39,72,147,235,226,233,186,120,121,58, -226,167,90,124,93,55,107,71,137,33,68,68,130,64,206,75,189,209,156,144,84, -44,141,3,8,137,187,178,156,80,211,26,110,242,100,230,146,120,121,8,48,76,6, -89,26,105,157,65,196,201,213,145,166,153,212,28,76,157,113,75,34,78,62,14, -38,73,105,228,142,136,178,48,141,152,228,73,150,83,0,148,39,137,75,67,73, -214,209,129,36,85,190,206,32,17,6,9,128,141,3,8,130,161,100,235,64,194,24, -52,41,73,19,189,200,108,201,19,111,181,2,232,66,239,173,37,230,157,244,56, -153,4,225,145,27,233,93,22,1,114,62,251,80,69,128,121,247,213,146,228,109, -79,190,212,17,35,106,125,246,78,164,68,68,111,175,23,217,45,13,33,119,208, -68,210,38,250,192,61,91,233,80,208,45,25,36,81,190,156,13,26,201,19,239, -162,2,214,66,31,125,153,226,64,13,27,236,72,96,130,68,62,251,48,68,196,153, -119,217,157,18,56,156,199,161,100,42,26,250,77,36,140,122,40,144,19,34,9, -24,246,103,139,172,150,56,125,145,1,17,29,44,112,250,183,0,100,24,200,218, -140,228,185,130,9,19,237,190,208,73,184,146,35,68,146,163,8,50,178,99,136, -44,89,196,2,33,70,64,208,196,67,74,226,88,17,105,73,24,186,37,40,38,5,133, -161,89,4,183,25,115,119,86,227,118,83,138,26,103,255,223,209,106,141,25,11, -244,95,117,56,208,159,250,223,251,250,45,52,13,250,47,186,156,104,79,253, -111,253,253,22,144,210,253,23,221,78,52,39,254,187,254,254,139,77,67,75, -244,95,117,56,208,159,250,239,251,250,45,22,141,23,209,125,212,227,66,127, -235,63,239,69,163,69,247,83,141,9,255,165,12,72,5,16,64,145,10,32,76,71,64, -156,217,161,180,34,6,64,208,198,36,78,50,20,20,92,204,50,44,147,32,134,226, -17,114,33,202,134,129,107,192,202,232,160,180,104,166,135,52,72,40,144,213, -33,178,152,26,34,56,163,105,44,104,146,116,139,77,43,34,98,57,38,116,72, -179,60,93,97,206,56,52,240,242,56,163,168,34,81,57,178,153,42,228,12,182, -58,22,66,89,19,57,68,176,74,68,35,104,195,18,239,116,102,114,94,100,104, -228,100,49,238,140,203,42,60,145,35,104,181,146,113,161,10,80,46,68,82,24, -245,145,132,108,228,148,54,100,137,64,34,13,100,153,222,1,40,6,33,223,20, -84,19,34,95,23,76,130,153,6,103,208,43,64,141,41,130,104,17,112,130,44,96, +DUK_INTERNAL const duk_uint8_t duk_strings_data[972] = { +79,40,209,144,168,105,6,78,54,139,89,185,44,48,46,90,120,8,154,140,35,103, +35,113,193,73,5,52,112,180,104,166,135,52,188,4,98,12,27,146,156,80,211,31, +129,115,150,64,52,220,109,24,18,68,156,24,38,67,114,36,55,9,119,151,132, +140,93,18,113,128,153,201,212,201,205,2,248,8,196,24,224,104,82,146,40,224, +193,48,114,168,37,147,196,54,123,28,4,98,12,43,148,67,103,177,192,70,32, +196,121,68,54,123,28,18,192,199,144,124,4,98,12,43,136,108,244,117,184,8, +196,24,95,40,134,207,71,91,128,140,65,133,113,13,158,158,151,1,24,131,11, +229,16,217,233,233,112,17,136,48,206,21,110,4,244,244,184,8,196,24,103,10, +183,2,122,218,156,4,98,12,24,203,112,64,179,113,193,79,8,218,155,131,32, +184,70,212,220,13,10,82,68,252,123,144,217,146,38,228,207,18,0,100,37,64, +178,212,11,161,17,104,162,96,10,200,193,57,165,65,169,16,5,100,81,27,70,18, +32,10,200,68,185,13,116,221,197,184,64,89,57,41,197,13,49,234,5,208,156, +113,87,55,118,147,20,187,56,161,166,92,221,212,73,210,236,226,134,153,115, +119,76,201,203,179,138,26,99,73,212,136,136,164,25,174,137,56,32,72,137, +101,23,52,45,13,34,86,9,79,136,104,201,114,149,96,52,138,134,140,151,75, +226,233,186,120,121,22,39,54,83,141,5,55,68,236,36,164,3,16,225,115,150,64, +52,205,163,2,72,154,83,138,26,99,75,12,11,150,103,5,36,20,211,70,140,133, +67,72,49,241,160,227,81,196,52,168,106,39,132,252,183,136,105,80,212,79,2, +249,110,128,126,88,95,133,109,237,237,237,151,235,127,46,249,119,203,190, +186,206,33,181,2,208,61,190,12,19,34,65,19,81,132,108,228,97,1,107,33,12, +32,45,100,137,64,247,175,9,19,155,41,198,130,155,134,69,146,100,227,226, +231,146,51,192,204,73,140,224,145,221,102,241,68,196,169,248,30,75,12,11, +151,242,233,187,143,138,24,137,162,164,255,253,63,3,201,97,129,114,254,92, +112,75,136,108,166,6,136,159,255,167,224,121,44,48,46,95,203,166,238,74, +113,67,77,201,128,223,255,223,224,121,44,48,46,95,203,145,46,9,205,16,39, +201,62,36,0,192,21,147,255,238,145,39,199,197,211,116,240,242,113,197,78, +214,211,226,233,187,107,105,19,119,37,56,161,166,52,221,212,201,205,36,240, +242,16,96,152,12,26,20,164,137,150,70,154,103,28,137,50,202,96,18,132,241, +41,104,105,56,218,48,36,138,183,57,56,128,68,24,38,2,52,12,34,10,133,147, +141,3,8,119,185,13,153,34,125,206,76,17,49,38,93,206,52,151,154,119,56,28, +76,130,112,200,141,206,21,209,96,23,35,238,114,160,139,0,243,238,114,78, +164,68,68,110,113,226,210,90,26,66,110,113,128,121,247,57,80,68,141,170, +183,56,84,52,11,70,73,19,110,114,160,93,8,113,57,143,66,200,84,53,244,154, +73,24,240,81,32,38,68,18,49,228,207,23,88,100,109,70,114,92,193,4,137,173, +168,36,220,73,19,247,247,182,168,209,144,187,223,58,156,104,79,190,183,127, +123,105,160,110,247,206,167,26,19,239,173,223,222,218,67,75,189,243,169, +198,132,251,235,183,247,182,154,134,151,123,231,83,141,9,247,215,111,239, +109,22,141,22,247,206,167,26,19,239,172,223,218,45,26,47,157,78,52,39,223, +74,24,144,10,32,129,34,20,64,152,142,129,57,179,67,104,68,12,129,161,140, +72,156,100,40,40,185,152,100,89,38,65,13,196,34,228,67,149,13,2,215,129, +149,209,65,104,209,77,14,104,144,81,33,170,67,101,48,52,68,113,70,210,88, +209,36,233,22,154,86,68,196,114,76,232,145,102,120,186,195,156,112,105,225, +228,113,71,80,68,162,115,101,50,85,200,25,108,116,44,132,178,38,114,137,96, +148,136,70,209,134,37,222,232,204,228,188,200,209,200,200,99,221,25,150,84, +121,34,70,209,107,36,227,66,20,160,92,136,164,49,235,35,8,217,201,40,108, +201,18,128,68,26,201,51,188,2,80,12,67,190,40,168,38,68,190,46,153,5,50,12, +207,160,86,129,26,83,4,208,34,225,4,88,192, }; #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_ROM_OBJECTS) -#error ROM support not enabled, rerun make_dist.py with --rom-support +#error ROM support not enabled, rerun configure.py with --rom-support #else /* DUK_USE_ROM_OBJECTS */ -/* native functions: 149 */ -DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { +/* native functions: 185 */ +DUK_INTERNAL const duk_c_function duk_bi_native_functions[185] = { + NULL, duk_bi_array_constructor, duk_bi_array_constructor_is_array, duk_bi_array_prototype_concat, @@ -9089,11 +10974,11 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_boolean_constructor, duk_bi_boolean_prototype_tostring_shared, duk_bi_buffer_compare_shared, - duk_bi_buffer_constructor, - duk_bi_buffer_prototype_tostring_shared, duk_bi_buffer_readfield, duk_bi_buffer_slice_shared, duk_bi_buffer_writefield, + duk_bi_cbor_decode, + duk_bi_cbor_encode, duk_bi_dataview_constructor, duk_bi_date_constructor, duk_bi_date_constructor_now, @@ -9104,6 +10989,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_date_prototype_set_shared, duk_bi_date_prototype_set_time, duk_bi_date_prototype_to_json, + duk_bi_date_prototype_toprimitive, duk_bi_date_prototype_tostring_shared, duk_bi_date_prototype_value_of, duk_bi_duktape_object_act, @@ -9126,6 +11012,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_function_prototype_apply, duk_bi_function_prototype_bind, duk_bi_function_prototype_call, + duk_bi_function_prototype_hasinstance, duk_bi_function_prototype_to_string, duk_bi_global_object_decode_uri, duk_bi_global_object_decode_uri_component, @@ -9137,20 +11024,20 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_global_object_is_nan, duk_bi_global_object_parse_float, duk_bi_global_object_parse_int, - duk_bi_global_object_print_helper, - duk_bi_global_object_require, duk_bi_global_object_unescape, duk_bi_json_object_parse, duk_bi_json_object_stringify, - duk_bi_logger_constructor, - duk_bi_logger_prototype_fmt, - duk_bi_logger_prototype_log_shared, - duk_bi_logger_prototype_raw, + duk_bi_math_object_clz32, + duk_bi_math_object_hypot, + duk_bi_math_object_imul, duk_bi_math_object_max, duk_bi_math_object_min, duk_bi_math_object_onearg_shared, duk_bi_math_object_random, + duk_bi_math_object_sign, duk_bi_math_object_twoarg_shared, + duk_bi_native_function_length, + duk_bi_native_function_name, duk_bi_nodejs_buffer_byte_length, duk_bi_nodejs_buffer_concat, duk_bi_nodejs_buffer_constructor, @@ -9161,6 +11048,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_nodejs_buffer_tojson, duk_bi_nodejs_buffer_tostring, duk_bi_nodejs_buffer_write, + duk_bi_number_check_shared, duk_bi_number_constructor, duk_bi_number_prototype_to_exponential, duk_bi_number_prototype_to_fixed, @@ -9169,624 +11057,705 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[149] = { duk_bi_number_prototype_to_string, duk_bi_number_prototype_value_of, duk_bi_object_constructor, + duk_bi_object_constructor_assign, duk_bi_object_constructor_create, duk_bi_object_constructor_define_properties, duk_bi_object_constructor_define_property, duk_bi_object_constructor_get_own_property_descriptor, + duk_bi_object_constructor_is, duk_bi_object_constructor_is_extensible, duk_bi_object_constructor_is_sealed_frozen_shared, duk_bi_object_constructor_keys_shared, duk_bi_object_constructor_prevent_extensions, duk_bi_object_constructor_seal_freeze_shared, duk_bi_object_getprototype_shared, + duk_bi_object_prototype_defineaccessor, duk_bi_object_prototype_has_own_property, duk_bi_object_prototype_is_prototype_of, + duk_bi_object_prototype_lookupaccessor, duk_bi_object_prototype_property_is_enumerable, duk_bi_object_prototype_to_locale_string, duk_bi_object_prototype_to_string, duk_bi_object_prototype_value_of, duk_bi_object_setprototype_shared, + duk_bi_performance_now, duk_bi_pointer_constructor, duk_bi_pointer_prototype_tostring_shared, duk_bi_proxy_constructor, + duk_bi_reflect_apply, + duk_bi_reflect_construct, + duk_bi_reflect_object_delete_property, + duk_bi_reflect_object_get, + duk_bi_reflect_object_has, + duk_bi_reflect_object_set, duk_bi_regexp_constructor, duk_bi_regexp_prototype_exec, + duk_bi_regexp_prototype_flags, + duk_bi_regexp_prototype_shared_getter, duk_bi_regexp_prototype_test, - duk_bi_regexp_prototype_to_string, + duk_bi_regexp_prototype_tostring, duk_bi_string_constructor, duk_bi_string_constructor_from_char_code, + duk_bi_string_constructor_from_code_point, duk_bi_string_prototype_caseconv_shared, duk_bi_string_prototype_char_at, duk_bi_string_prototype_char_code_at, duk_bi_string_prototype_concat, + duk_bi_string_prototype_includes, duk_bi_string_prototype_indexof_shared, duk_bi_string_prototype_locale_compare, duk_bi_string_prototype_match, + duk_bi_string_prototype_repeat, duk_bi_string_prototype_replace, duk_bi_string_prototype_search, duk_bi_string_prototype_slice, duk_bi_string_prototype_split, + duk_bi_string_prototype_startswith_endswith, duk_bi_string_prototype_substr, duk_bi_string_prototype_substring, duk_bi_string_prototype_to_string, duk_bi_string_prototype_trim, + duk_bi_symbol_constructor_shared, + duk_bi_symbol_key_for, + duk_bi_symbol_toprimitive, + duk_bi_symbol_tostring_shared, + duk_bi_textdecoder_constructor, + duk_bi_textdecoder_prototype_decode, + duk_bi_textdecoder_prototype_shared_getter, + duk_bi_textencoder_constructor, + duk_bi_textencoder_prototype_encode, + duk_bi_textencoder_prototype_encoding_getter, duk_bi_thread_constructor, duk_bi_thread_current, duk_bi_thread_resume, duk_bi_thread_yield, duk_bi_type_error_thrower, + duk_bi_typedarray_buffer_getter, + duk_bi_typedarray_bytelength_getter, + duk_bi_typedarray_byteoffset_getter, duk_bi_typedarray_constructor, duk_bi_typedarray_set, + duk_bi_uint8array_allocplain, + duk_bi_uint8array_plainof, }; -#if defined(DUK_USE_BUILTIN_INITJS) -DUK_INTERNAL const duk_uint8_t duk_initjs_data[187] = { -40,102,117,110,99,116,105,111,110,40,100,44,97,41,123,102,117,110,99,116, -105,111,110,32,98,40,97,44,98,44,99,41,123,79,98,106,101,99,116,46,100,101, -102,105,110,101,80,114,111,112,101,114,116,121,40,97,44,98,44,123,118,97, -108,117,101,58,99,44,119,114,105,116,97,98,108,101,58,33,48,44,101,110,117, -109,101,114,97,98,108,101,58,33,49,44,99,111,110,102,105,103,117,114,97,98, -108,101,58,33,48,125,41,125,98,40,97,46,76,111,103,103,101,114,44,34,99, -108,111,103,34,44,110,101,119,32,97,46,76,111,103,103,101,114,40,34,67,34, -41,41,59,98,40,97,44,34,109,111,100,76,111,97,100,101,100,34,44,123,125,41, -125,41,40,116,104,105,115,44,68,117,107,116,97,112,101,41,59,10,0, -}; -#endif /* DUK_USE_BUILTIN_INITJS */ #if defined(DUK_USE_DOUBLE_LE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = { -105,195,75,32,3,148,52,154,248,9,26,13,128,112,105,0,240,22,20,26,95,124,6, -152,52,137,0,120,99,74,239,129,18,70,241,191,2,98,13,79,32,42,88,210,90,2, -240,1,50,141,37,168,76,94,216,118,69,229,203,127,44,0,84,163,73,106,21,75, -14,236,249,98,242,229,191,150,0,46,81,164,181,14,165,151,54,94,89,119,99, -203,23,151,45,252,176,1,146,141,37,168,93,63,59,186,97,241,23,151,45,252, -176,1,178,141,37,168,77,79,60,50,197,229,203,127,44,0,116,163,73,106,17,86, -148,152,188,185,111,229,128,15,148,129,198,137,36,58,166,142,91,251,212, -243,195,44,94,92,183,242,13,79,8,45,14,91,252,121,148,52,199,120,63,72,105, -21,240,118,128,210,237,224,245,17,165,43,224,211,55,231,207,151,148,161,70, -145,0,31,40,107,26,2,18,138,26,228,192,142,0,16,161,174,76,9,74,26,228,192, -158,0,8,161,174,76,10,96,2,42,26,228,192,174,0,26,161,174,76,11,96,3,74,26, -228,192,190,0,44,161,174,76,12,96,3,202,26,228,192,206,0,70,161,169,84,14, -202,3,255,254,32,234,0,0,0,0,0,0,7,195,248,119,0,0,0,0,0,0,3,193,252,57, -136,1,152,32,16,194,0,166,24,6,49,0,57,138,2,12,96,18,99,128,163,32,5,153, -40,76,94,216,118,69,229,203,127,35,41,10,165,135,118,124,177,121,114,223, -200,203,67,169,101,205,151,150,93,216,242,197,229,203,127,35,49,11,167,231, -119,76,62,34,242,229,191,145,154,132,212,243,195,44,94,92,183,242,51,144, -138,180,164,197,229,203,127,35,60,6,26,0,52,208,193,226,117,215,211,15,12, -166,146,11,67,150,255,30,77,24,58,113,64,243,92,8,27,0,68,217,130,70,212, -19,54,224,161,185,5,77,216,44,111,65,115,126,12,28,16,100,225,156,16,32,18, -17,195,15,46,121,100,238,232,136,136,87,12,60,185,229,141,179,126,30,136, -100,130,233,231,59,12,228,34,66,52,243,141,167,118,158,153,80,73,9,201,151, -30,252,153,106,210,146,118,72,150,76,184,247,228,203,86,148,152,123,246, -240,223,187,46,238,135,132,132,229,221,143,126,76,181,105,73,61,36,75,46, -236,123,242,101,171,74,76,61,251,120,111,221,151,119,67,226,65,178,243,199, -135,134,83,242,66,58,238,203,207,30,30,25,81,201,5,225,203,78,238,136,163, -208,92,59,50,242,232,138,62,0,2,38,163,19,255,255,224,142,80,192,0,20,31, -240,14,135,103,203,210,135,45,253,55,244,243,195,44,252,205,197,0,1,18,221, -82,0,3,24,207,151,164,254,251,168,114,223,195,47,46,158,98,101,231,143,150, -158,29,55,242,104,68,79,62,94,147,251,238,161,203,127,12,188,186,121,157, -135,110,94,109,100,131,99,229,151,15,76,172,168,8,89,217,16,201,151,54,157, -217,104,114,223,195,47,46,154,114,243,102,68,19,158,92,59,27,73,6,205,203, -46,95,89,91,74,0,3,17,225,203,47,108,187,186,69,241,211,46,238,122,119,238, -230,216,72,70,158,116,242,225,217,151,35,81,33,26,121,198,229,191,214,93, -205,69,0,1,134,105,231,23,199,76,187,185,233,197,179,43,73,32,154,242,249, -230,214,80,0,31,255,193,2,38,103,110,117,24,81,115,0,78,228,0,161,208,16, -237,24,121,207,239,186,135,45,252,50,242,233,229,188,144,221,60,232,114, -223,211,127,79,60,50,207,204,224,72,167,14,91,248,101,229,211,204,158,113, -119,117,219,151,150,28,91,50,184,144,40,95,224,0,15,248,64,4,20,78,129,5, -195,195,134,207,38,232,130,99,195,179,97,201,244,19,22,157,217,14,15,130, -135,254,0,48,125,60,224,242,229,135,200,9,1,255,12,2,162,136,112,2,112,80, -128,0,193,177,239,221,143,15,64,35,224,152,20,144,62,27,248,3,2,9,195,175, -61,0,231,208,126,89,123,101,229,207,40,72,32,188,244,105,205,208,40,16,94, -123,52,227,202,22,136,39,61,252,186,6,18,13,207,134,205,56,242,134,175,65, -250,238,231,163,78,110,129,231,208,125,59,178,101,241,63,48,25,248,0,12,47, -102,30,125,36,238,201,151,196,252,192,103,255,255,240,92,189,178,242,242,8, -105,4,231,191,110,80,67,80,0,24,62,109,252,162,225,199,160,16,212,0,10,7, -183,15,0,67,80,0,56,54,109,59,58,101,228,8,106,0,9,6,229,151,39,92,121,66, -15,192,0,97,124,178,228,235,143,45,45,57,244,116,8,63,255,255,10,39,248,0, -195,51,114,223,182,30,140,60,161,239,201,149,248,248,31,241,0,140,80,129, -202,10,49,128,10,35,1,6,199,163,15,40,61,32,9,10,199,163,15,40,123,242,101, -131,210,4,144,108,123,247,99,195,210,8,250,15,167,118,76,190,39,230,131,52, -133,236,195,207,164,157,217,50,248,159,154,12,212,0,6,27,179,126,60,59,50, -195,223,183,134,30,89,97,9,5,219,135,166,61,16,164,131,242,203,195,102,28, -121,97,145,6,231,151,15,44,122,33,201,5,231,179,78,60,177,8,130,243,225, -179,79,72,148,66,121,245,197,207,167,45,59,179,197,162,23,211,124,205,253, -242,242,135,135,158,87,240,68,122,111,153,191,30,29,153,102,111,239,151, -148,60,60,242,191,130,23,211,125,94,28,50,242,135,135,158,87,240,128,0,196, -122,111,153,191,30,29,153,106,240,225,151,148,60,60,242,191,132,0,6,9,211, -150,157,177,160,131,115,235,139,159,78,81,72,10,47,248,0,3,254,40,17,138, -48,66,136,152,64,0,66,129,48,5,27,252,88,76,216,54,47,214,131,50,172,88,31, -255,255,255,255,255,253,239,240,153,178,103,95,173,6,101,88,176,0,64,0,0,0, -0,0,0,3,168,0,0,0,0,0,0,31,15,241,26,19,233,201,169,38,180,91,242,103,70, -147,58,77,75,48,0,0,0,0,0,0,60,31,226,51,162,199,131,82,77,104,183,228,206, -141,38,116,154,150,96,0,0,0,0,0,0,120,127,128,15,248,192,70,40,0,0,0,0,0,0, -0,0,3,10,44,68,9,216,8,20,49,130,15,211,124,109,62,50,228,95,36,55,166,248, -190,56,111,221,151,119,77,56,118,47,18,23,211,125,14,89,113,233,231,167, -126,230,18,5,31,252,0,224,188,48,242,231,148,116,144,58,181,33,143,127,64, -247,111,238,56,0,127,199,2,49,72,0,0,0,0,0,0,248,127,180,81,36,4,51,166, -248,152,122,101,167,211,150,157,217,201,2,0,3,12,233,190,166,157,185,105, -244,229,167,118,114,64,128,1,4,228,129,0,3,137,116,223,51,126,60,59,50,196, -195,211,45,62,156,180,238,206,72,16,0,72,151,77,243,55,227,195,179,45,77, -59,114,211,233,203,78,236,228,129,0,5,10,73,2,0,12,21,18,4,0,28,82,35,32, -80,74,8,62,124,189,42,105,219,148,148,16,188,249,122,70,235,179,101,156, -184,121,15,132,0,34,29,159,47,74,181,33,198,235,179,101,156,184,121,15,132, -0,38,17,159,47,73,187,247,116,208,62,16,0,168,94,124,189,42,212,135,55,126, -238,154,7,194,0,23,7,207,151,164,76,61,50,143,132,0,50,21,159,47,74,181,33, -196,195,211,40,248,64,3,96,217,242,244,137,135,200,248,64,3,161,57,242,244, -171,82,28,76,62,71,194,0,31,8,207,151,164,141,253,121,115,31,8,0,132,47,62, -94,149,106,67,145,191,175,46,99,225,0,17,133,103,203,210,110,157,221,122, -101,230,62,16,1,40,110,124,189,42,212,135,55,78,238,189,50,243,31,8,0,156, -43,62,94,148,242,227,223,187,39,49,240,128,10,67,115,229,233,86,164,58,121, -113,239,221,147,152,248,64,5,97,249,242,244,155,167,102,205,60,242,227,223, -187,39,49,240,128,11,68,179,229,233,86,164,57,186,118,108,211,207,46,61, -251,178,115,31,8,0,188,71,62,94,149,52,237,203,235,126,236,179,243,102,231, -151,161,0,32,252,242,244,169,167,110,82,34,67,249,229,233,55,78,205,154, -121,229,199,191,118,78,100,37,0,24,137,115,203,210,173,72,115,116,236,217, -167,158,92,123,247,100,230,66,80,1,152,87,60,189,41,229,199,191,118,78,100, -43,224,3,80,222,121,122,85,169,14,158,92,123,247,100,230,66,190,0,55,10, -231,151,164,221,59,186,244,203,204,133,252,0,114,27,207,47,74,181,33,205, -211,187,175,76,188,200,95,192,7,97,28,242,244,145,191,175,46,100,51,224,3, -208,190,121,122,85,169,14,70,254,188,185,144,207,128,15,193,249,229,233,19, -15,76,164,37,0,32,133,115,203,210,173,72,113,48,244,202,66,80,2,24,71,60, -189,38,239,221,211,65,10,248,1,20,47,158,94,149,106,67,155,191,119,77,4,43, -224,4,112,190,121,122,70,235,179,101,156,184,121,16,191,128,18,67,185,229, -233,86,164,56,221,118,108,179,151,15,34,23,240,2,88,62,124,189,44,229,195, -200,124,32,4,208,126,121,122,89,203,135,145,9,64,9,194,145,254,0,0,255,144, -24,100,130,14,0,16,176,2,192,129,11,33,12,1,168,193,108,96,186,48,95,32,0, -0,0,0,0,0,0,0,56,38,95,25,113,189,18,9,211,47,62,143,100,20,95,0,20,159, -240,0,7,252,144,162,241,2,195,66,7,11,89,204,140,197,252,229,197,226,230, -115,3,16,69,19,64,5,43,252,0,9,255,40,16,188,33,49,123,97,217,23,151,45, -252,131,66,7,0,20,191,240,0,39,252,176,66,240,133,82,195,187,62,88,188,185, -111,228,26,16,56,0,166,127,128,1,63,230,2,23,132,58,150,92,217,121,101,221, -143,44,94,92,183,242,13,8,28,0,83,127,192,0,159,243,65,11,194,23,79,206, -238,152,124,69,229,203,127,32,208,129,192,5,59,252,0,9,255,56,16,188,33,53, -60,240,203,23,151,45,252,131,66,7,0,20,255,240,0,39,252,240,66,240,132,85, -165,38,47,46,91,249,6,132,14,0,31,255,228,64,98,192,105,87,20,139,10,191,5, -64,130,76,156,197,132,1,101,91,91,187,22,176,36,8,28,201,204,160,119,156, -253,127,33,23,115,31,193,102,79,142,202,44,15,232,34,182,84,113,95,115,248, -52,201,241,216,176,139,0,59,148,152,85,239,47,108,254,5,66,76,1,130,212,69, -79,178,16,148,8,61,58,52,170,49,190,202,6,105,219,251,52,245,7,49,252,22, -157,26,85,25,64,205,59,127,102,158,160,246,63,74,7,135,23,53,2,65,48,227, -223,205,64,160,0,48,76,60,244,238,80,40,0,20,19,15,76,59,148,10,0,7,5,195, -211,14,230,74,72,130,99,203,167,98,129,64,1,32,120,247,243,80,40,0,44,15, -47,142,10,5,0,6,130,230,217,191,127,37,2,128,3,192,246,111,206,160,80,0, -136,30,220,62,19,151,160,123,116,238,79,94,129,240,223,221,73,32,0,48,110, -88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0, -21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23, -134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0, -191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,0,0,0,0,64,65,98, -32,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144, -60,56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7, -147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3, -252,131,32,248,250,242,229,151,119,72,240,3,254,148,0,2,168,254,0,0,255, -167,0,33,68,88,32,0,33,64,176,2,170,254,0,0,255,169,0,33,69,220,32,0,33,67, -184,2,172,254,0,0,255,171,8,137,144,0,0,0,0,0,0,0,128,68,73,4,195,187,126, -226,8,4,178,16,41,164,32,147,7,136,52,193,240,0,18,17,48,124,0,8,133,76,31, -0,3,33,147,7,192,1,8,116,193,240,0,82,127,255,132,47,65,11,137,191,174,45, -153,98,242,229,191,144,105,4,95,47,46,91,249,32,211,185,6,94,92,183,242,65, -163,14,236,155,52,238,206,0,85,255,192,6,13,167,157,109,57,123,136,144,31, -245,192,3,5,231,179,78,60,163,9,0,2,10,199,248,0,3,254,192,4,32,249,242, -244,147,187,163,129,116,128,24,66,51,229,233,87,78,238,142,5,210,0,65,8, -207,151,164,157,221,24,182,23,72,1,140,39,62,94,149,116,238,232,197,176, -186,64,8,97,25,242,244,147,187,163,54,66,233,0,50,132,231,203,210,174,157, -221,25,178,23,72,1,20,43,62,94,145,182,111,195,209,155,33,116,128,17,194, -179,229,233,27,102,252,61,27,52,23,72,1,36,31,158,94,146,119,116,112,50, -208,3,8,71,60,189,42,233,221,209,192,203,64,8,33,28,242,244,147,187,163,22, -195,45,0,49,132,243,203,210,174,157,221,24,182,25,104,1,12,35,158,94,146, -119,116,102,200,101,160,6,80,158,121,122,85,211,187,163,54,67,45,0,34,133, -115,203,210,54,205,248,122,51,100,50,208,2,56,87,60,189,35,108,223,135,163, -102,131,45,0,36,7,255,248,1,11,50,136,132,115,235,139,15,46,88,124,140,36, -0,4,43,79,224,139,16,0,0,0,0,0,0,60,15,192,101,253,152,0,5,109,252,17,98,0, -0,0,0,0,0,7,129,248,12,191,181,0,0,174,63,130,44,64,0,0,0,0,0,0,240,63,1, -151,246,224,0,21,215,240,69,136,0,0,0,0,0,0,0,8,0,50,254,228,0,2,188,254,8, -177,0,0,0,0,0,0,0,1,0,6,95,221,128,0,87,223,193,22,32,0,0,0,0,0,0,8,32,0, -203,251,208,0,11,3,248,34,196,0,0,0,0,0,0,1,4,0,25,127,126,0,1,97,127,4,88, -128,0,0,0,0,0,0,32,128,3,47,240,64,0,44,79,224,139,16,0,0,0,0,0,0,8,16,0, -101,254,24,0,5,141,252,1,96,216,247,238,199,135,162,162,33,90,121,197,221, -143,126,77,59,179,172,146,17,167,156,46,185,179,101,228,176,65,89,77,16, -124,123,246,240,195,203,40,162,64,0,193,255,138,5,144,158,89,112,228,171, -39,119,71,2,232,132,114,203,135,36,157,221,28,11,164,0,66,25,203,46,28,149, -100,238,232,197,180,200,162,233,0,1,134,114,203,135,37,89,59,186,49,109,10, -40,186,64,2,97,124,178,225,201,39,119,70,45,166,69,23,72,0,140,47,150,92, -57,36,238,232,197,180,40,162,233,0,25,134,114,203,135,37,89,59,186,51,101, -50,40,186,64,0,161,156,178,225,201,86,78,238,140,217,66,138,46,144,0,168, -95,44,184,114,73,221,209,155,41,145,69,210,0,37,11,229,151,14,73,59,186,51, -101,10,40,186,64,6,161,124,178,225,201,27,102,252,61,38,69,23,72,0,28,47, -150,92,57,35,108,223,135,164,40,162,233,0,11,134,114,203,135,36,77,253,113, -108,203,50,40,186,64,1,33,156,178,225,201,19,127,92,91,50,194,138,46,144,0, -200,87,44,184,114,85,147,187,164,200,162,237,0,5,133,114,203,135,37,89,59, -186,66,138,46,208,0,216,79,44,184,114,73,221,210,100,81,118,128,10,194,121, -101,195,146,78,238,144,162,139,180,0,118,21,223,150,158,153,106,201,221, -209,192,203,33,61,249,105,233,150,78,238,142,6,90,0,33,13,239,203,79,76, -181,100,238,232,197,180,200,163,45,0,1,134,247,229,167,166,90,178,119,116, -98,218,20,81,150,128,4,195,59,242,211,211,44,157,221,24,182,153,20,101,160, -2,48,206,252,180,244,203,39,119,70,45,161,69,25,104,0,204,55,191,45,61,50, -213,147,187,163,54,83,34,140,180,0,10,27,223,150,158,153,106,201,221,209, -155,40,81,70,90,0,21,12,239,203,79,76,178,119,116,102,202,100,81,150,128,9, -67,59,242,211,211,44,157,221,25,178,133,20,101,160,3,80,206,252,180,244, -203,27,102,252,61,38,69,25,104,0,28,51,191,45,61,50,198,217,191,15,72,81, -70,90,0,23,13,239,203,79,76,177,55,245,197,179,44,200,163,45,0,4,134,247, -229,167,166,88,155,250,226,217,150,20,81,150,128,6,66,251,242,211,211,45, -89,59,186,76,138,51,16,0,88,95,126,90,122,101,171,39,119,72,81,70,98,0,27, -10,239,203,79,76,178,119,116,153,20,102,32,2,176,174,252,180,244,203,39, -119,72,81,70,98,0,58,40,173,176,82,90,4,19,54,157,155,21,217,6,203,199,174, -29,156,197,9,7,199,191,111,12,60,178,138,20,0,6,9,143,127,15,42,208,130, -243,217,167,30,81,132,65,123,242,211,211,42,228,0, +DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { +144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, +124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, +167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, +64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, +142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, +242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, +1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, +33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, +198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, +10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, +53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, +84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, +109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, +11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, +145,153,136,0,0,0,0,0,0,31,15,249,152,0,0,0,0,0,0,30,15,249,120,144,13,96, +155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23, +194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, +137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, +36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, +209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, +144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, +122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, +136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, +195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, +17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, +2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, +35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, +174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, +0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, +73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, +89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, +144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, +78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, +196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, +132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, +162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, +160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, +151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, +113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, +129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, +168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, +153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, +36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, +203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, +136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, +207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, +37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, +44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, +231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, +30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, +196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, +35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, +34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, +120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, +68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, +228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, +18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, +249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, +102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, +9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, +255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, +57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, +130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, +11,161,32,127,255,255,255,255,255,247,191,137,235,16,221,170,129,116,36,0, +16,0,0,0,0,0,0,12,196,0,0,0,0,0,0,15,135,242,61,123,164,137,162,164,218,67, +74,134,162,120,128,0,0,0,0,0,1,224,254,71,173,33,129,52,84,155,72,105,80, +212,79,16,0,0,0,0,0,0,60,63,195,244,143,146,22,230,192,0,0,0,0,0,0,176,60, +33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,255,255,255,255,159,161, +144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,255,255,255,255,207, +240,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34, +92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, +194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, +0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, +18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, +5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0,0, +0,0,0,248,127,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, +185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, +130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, +197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, +12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, +218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, +98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, +250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, +40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, +49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, +56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, +173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, +168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, +18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, +210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, +72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, +72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, +186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, +50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, +74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, +186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, +162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, +55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, +48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, +82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, +144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, +221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, +105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, +137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, +23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, +19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, +72,115,96,0,0,0,0,0,15,106,32,91,60,165,195,201,194,8,134,149,216,162,0, +192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, +195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, +1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, +36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, +0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, +102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, +20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, +216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, +81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, +166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, +20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, +68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, +159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, +67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, +173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, +140,35,103,0,0,0,0,0,0,3,192,252,206,25,228,35,208,226,100,150,211,201,29, +162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,25,244,35,208,226,100,150,211, +201,29,162,44,140,35,103,0,0,0,0,0,0,3,192,252,206,26,4,35,208,226,100,150, +211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,20,35,208,226,100, +150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,1,0,206,26,36,35,208,226, +100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,52,35,208, +226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,68,35, +208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,65,0,206,26,84, +35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,0,0,0,0,129,0,195, +154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, +25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, +235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, +196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, +200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, +213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, +183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, +221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, +31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, +18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, +59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, +39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, +73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, +61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, +67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, +167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, +68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, +193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, +178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, +157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, +81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, +133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, +22,209,68,201,187,129,4,2,8,3,132,64,60,36,6,149,113,72,176,171,240,84,0, +157,91,116,116,32,11,42,218,221,216,181,129,32,3,234,219,165,3,188,231,235, +249,8,187,152,252,47,86,227,105,18,7,244,17,91,42,56,175,185,248,110,173, +198,209,208,36,0,238,82,97,87,188,189,179,240,93,122,32,12,22,162,42,125, +144,132,160,7,236,161,25,232,237,105,64,205,59,127,102,158,160,230,63,11, +217,66,51,210,129,154,118,254,205,61,65,236,127,171,197,34,168,48,6,90,194, +1,0,39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0, +65,6,51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88, +80,0,201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25, +69,234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176, +165,1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230, +107,64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146, +132,103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17, +145,52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0, +104,146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128, +56,18,52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141, +47,129,6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69, +15,155,163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254, +36,3,17,46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108, +248,75,204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2, +206,9,113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2, +178,66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136, +38,232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55, +38,3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37, +202,160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248, +0,0,179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11, +181,192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218, +121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,153,188,56,132,122,28,76, +146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,120,31,168,160,45,110,23, +30,176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56, +153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,240,63,51,120,145,8,244, +56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,161,8, +244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,0,64,51,120,177, +8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51,120, +193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64,51, +120,209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,16,64, +51,120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,32, +64,32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16, +137,112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120, +34,74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66, +8,35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240, +117,96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82, +32,148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81, +238,2,3,107,173,218,3,192, }; #elif defined(DUK_USE_DOUBLE_BE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = { -105,195,75,32,3,148,52,154,248,9,26,13,128,112,105,0,240,22,20,26,95,124,6, -152,52,137,0,120,99,74,239,129,18,70,241,191,2,98,13,79,32,42,88,210,90,2, -240,1,50,141,37,168,76,94,216,118,69,229,203,127,44,0,84,163,73,106,21,75, -14,236,249,98,242,229,191,150,0,46,81,164,181,14,165,151,54,94,89,119,99, -203,23,151,45,252,176,1,146,141,37,168,93,63,59,186,97,241,23,151,45,252, -176,1,178,141,37,168,77,79,60,50,197,229,203,127,44,0,116,163,73,106,17,86, -148,152,188,185,111,229,128,15,148,129,198,137,36,58,166,142,91,251,212, -243,195,44,94,92,183,242,13,79,8,45,14,91,252,121,148,52,199,120,63,72,105, -21,240,118,128,210,237,224,245,17,165,43,224,211,55,231,207,151,148,161,70, -145,0,31,40,107,26,2,18,138,26,228,192,142,0,16,161,174,76,9,74,26,228,192, -158,0,8,161,174,76,10,96,2,42,26,228,192,174,0,26,161,174,76,11,96,3,74,26, -228,192,190,0,44,161,174,76,12,96,3,202,26,228,192,206,0,70,161,169,84,14, -202,3,255,254,32,234,3,255,192,0,0,0,0,0,0,119,1,255,192,0,0,0,0,0,0,57, -136,1,152,32,16,194,0,166,24,6,49,0,57,138,2,12,96,18,99,128,163,32,5,153, -40,76,94,216,118,69,229,203,127,35,41,10,165,135,118,124,177,121,114,223, -200,203,67,169,101,205,151,150,93,216,242,197,229,203,127,35,49,11,167,231, -119,76,62,34,242,229,191,145,154,132,212,243,195,44,94,92,183,242,51,144, -138,180,164,197,229,203,127,35,60,6,26,0,52,208,193,226,117,215,211,15,12, -166,146,11,67,150,255,30,77,24,58,113,64,243,92,8,27,0,68,217,130,70,212, -19,54,224,161,185,5,77,216,44,111,65,115,126,12,28,16,100,225,156,16,32,18, -17,195,15,46,121,100,238,232,136,136,87,12,60,185,229,141,179,126,30,136, -100,130,233,231,59,12,228,34,66,52,243,141,167,118,158,153,80,73,9,201,151, -30,252,153,106,210,146,118,72,150,76,184,247,228,203,86,148,152,123,246, -240,223,187,46,238,135,132,132,229,221,143,126,76,181,105,73,61,36,75,46, -236,123,242,101,171,74,76,61,251,120,111,221,151,119,67,226,65,178,243,199, -135,134,83,242,66,58,238,203,207,30,30,25,81,201,5,225,203,78,238,136,163, -208,92,59,50,242,232,138,62,0,2,38,163,19,255,255,224,142,80,192,0,20,31, -240,14,135,103,203,210,135,45,253,55,244,243,195,44,252,205,197,0,1,18,221, -82,0,3,24,207,151,164,254,251,168,114,223,195,47,46,158,98,101,231,143,150, -158,29,55,242,104,68,79,62,94,147,251,238,161,203,127,12,188,186,121,157, -135,110,94,109,100,131,99,229,151,15,76,172,168,8,89,217,16,201,151,54,157, -217,104,114,223,195,47,46,154,114,243,102,68,19,158,92,59,27,73,6,205,203, -46,95,89,91,74,0,3,17,225,203,47,108,187,186,69,241,211,46,238,122,119,238, -230,216,72,70,158,116,242,225,217,151,35,81,33,26,121,198,229,191,214,93, -205,69,0,1,134,105,231,23,199,76,187,185,233,197,179,43,73,32,154,242,249, -230,214,80,0,31,255,193,2,38,103,110,117,24,81,115,0,78,228,0,161,208,16, -237,24,121,207,239,186,135,45,252,50,242,233,229,188,144,221,60,232,114, -223,211,127,79,60,50,207,204,224,72,167,14,91,248,101,229,211,204,158,113, -119,117,219,151,150,28,91,50,184,144,40,95,224,0,15,248,64,4,20,78,129,5, -195,195,134,207,38,232,130,99,195,179,97,201,244,19,22,157,217,14,15,130, -135,254,0,48,125,60,224,242,229,135,200,9,1,255,12,2,162,136,112,2,112,80, -128,0,193,177,239,221,143,15,64,35,224,152,20,144,62,27,248,3,2,9,195,175, -61,0,231,208,126,89,123,101,229,207,40,72,32,188,244,105,205,208,40,16,94, -123,52,227,202,22,136,39,61,252,186,6,18,13,207,134,205,56,242,134,175,65, -250,238,231,163,78,110,129,231,208,125,59,178,101,241,63,48,25,248,0,12,47, -102,30,125,36,238,201,151,196,252,192,103,255,255,240,92,189,178,242,242,8, -105,4,231,191,110,80,67,80,0,24,62,109,252,162,225,199,160,16,212,0,10,7, -183,15,0,67,80,0,56,54,109,59,58,101,228,8,106,0,9,6,229,151,39,92,121,66, -15,192,0,97,124,178,228,235,143,45,45,57,244,116,8,63,255,255,10,39,248,0, -195,51,114,223,182,30,140,60,161,239,201,149,248,248,31,241,0,140,80,129, -202,10,49,128,10,35,1,6,199,163,15,40,61,32,9,10,199,163,15,40,123,242,101, -131,210,4,144,108,123,247,99,195,210,8,250,15,167,118,76,190,39,230,131,52, -133,236,195,207,164,157,217,50,248,159,154,12,212,0,6,27,179,126,60,59,50, -195,223,183,134,30,89,97,9,5,219,135,166,61,16,164,131,242,203,195,102,28, -121,97,145,6,231,151,15,44,122,33,201,5,231,179,78,60,177,8,130,243,225, -179,79,72,148,66,121,245,197,207,167,45,59,179,197,162,23,211,124,205,253, -242,242,135,135,158,87,240,68,122,111,153,191,30,29,153,102,111,239,151, -148,60,60,242,191,130,23,211,125,94,28,50,242,135,135,158,87,240,128,0,196, -122,111,153,191,30,29,153,106,240,225,151,148,60,60,242,191,132,0,6,9,211, -150,157,177,160,131,115,235,139,159,78,81,72,10,47,248,0,3,254,40,17,138, -48,66,136,152,64,0,66,129,48,5,27,252,88,76,216,54,47,214,131,50,172,88,15, -253,255,255,255,255,255,255,240,153,178,103,95,173,6,101,88,176,0,0,0,0,0, -0,0,0,67,168,15,255,0,0,0,0,0,0,17,26,19,233,201,169,38,180,91,242,103,70, -147,58,77,75,48,31,252,0,0,0,0,0,0,34,51,162,199,131,82,77,104,183,228,206, -141,38,116,154,150,96,127,248,0,0,0,0,0,0,0,15,248,192,70,40,0,0,0,0,0,0,0, -0,3,10,44,68,9,216,8,20,49,130,15,211,124,109,62,50,228,95,36,55,166,248, -190,56,111,221,151,119,77,56,118,47,18,23,211,125,14,89,113,233,231,167, -126,230,18,5,31,252,0,224,188,48,242,231,148,116,144,58,181,33,143,127,64, -247,111,238,56,0,127,199,2,49,72,127,248,0,0,0,0,0,0,180,81,36,4,51,166, -248,152,122,101,167,211,150,157,217,201,2,0,3,12,233,190,166,157,185,105, -244,229,167,118,114,64,128,1,4,228,129,0,3,137,116,223,51,126,60,59,50,196, -195,211,45,62,156,180,238,206,72,16,0,72,151,77,243,55,227,195,179,45,77, -59,114,211,233,203,78,236,228,129,0,5,10,73,2,0,12,21,18,4,0,28,82,35,32, -80,74,8,62,124,189,42,105,219,148,148,16,188,249,122,70,235,179,101,156, -184,121,15,132,0,34,29,159,47,74,181,33,198,235,179,101,156,184,121,15,132, -0,38,17,159,47,73,187,247,116,208,62,16,0,168,94,124,189,42,212,135,55,126, -238,154,7,194,0,23,7,207,151,164,76,61,50,143,132,0,50,21,159,47,74,181,33, -196,195,211,40,248,64,3,96,217,242,244,137,135,200,248,64,3,161,57,242,244, -171,82,28,76,62,71,194,0,31,8,207,151,164,141,253,121,115,31,8,0,132,47,62, -94,149,106,67,145,191,175,46,99,225,0,17,133,103,203,210,110,157,221,122, -101,230,62,16,1,40,110,124,189,42,212,135,55,78,238,189,50,243,31,8,0,156, -43,62,94,148,242,227,223,187,39,49,240,128,10,67,115,229,233,86,164,58,121, -113,239,221,147,152,248,64,5,97,249,242,244,155,167,102,205,60,242,227,223, -187,39,49,240,128,11,68,179,229,233,86,164,57,186,118,108,211,207,46,61, -251,178,115,31,8,0,188,71,62,94,149,52,237,203,235,126,236,179,243,102,231, -151,161,0,32,252,242,244,169,167,110,82,34,67,249,229,233,55,78,205,154, -121,229,199,191,118,78,100,37,0,24,137,115,203,210,173,72,115,116,236,217, -167,158,92,123,247,100,230,66,80,1,152,87,60,189,41,229,199,191,118,78,100, -43,224,3,80,222,121,122,85,169,14,158,92,123,247,100,230,66,190,0,55,10, -231,151,164,221,59,186,244,203,204,133,252,0,114,27,207,47,74,181,33,205, -211,187,175,76,188,200,95,192,7,97,28,242,244,145,191,175,46,100,51,224,3, -208,190,121,122,85,169,14,70,254,188,185,144,207,128,15,193,249,229,233,19, -15,76,164,37,0,32,133,115,203,210,173,72,113,48,244,202,66,80,2,24,71,60, -189,38,239,221,211,65,10,248,1,20,47,158,94,149,106,67,155,191,119,77,4,43, -224,4,112,190,121,122,70,235,179,101,156,184,121,16,191,128,18,67,185,229, -233,86,164,56,221,118,108,179,151,15,34,23,240,2,88,62,124,189,44,229,195, -200,124,32,4,208,126,121,122,89,203,135,145,9,64,9,194,145,254,0,0,255,144, -24,100,130,14,0,16,176,2,192,129,11,33,12,1,168,193,108,96,186,48,95,32,0, -0,0,0,0,0,0,0,56,38,95,25,113,189,18,9,211,47,62,143,100,20,95,0,20,159, -240,0,7,252,144,162,241,2,195,66,7,11,89,204,140,197,252,229,197,226,230, -115,3,16,69,19,64,5,43,252,0,9,255,40,16,188,33,49,123,97,217,23,151,45, -252,131,66,7,0,20,191,240,0,39,252,176,66,240,133,82,195,187,62,88,188,185, -111,228,26,16,56,0,166,127,128,1,63,230,2,23,132,58,150,92,217,121,101,221, -143,44,94,92,183,242,13,8,28,0,83,127,192,0,159,243,65,11,194,23,79,206, -238,152,124,69,229,203,127,32,208,129,192,5,59,252,0,9,255,56,16,188,33,53, -60,240,203,23,151,45,252,131,66,7,0,20,255,240,0,39,252,240,66,240,132,85, -165,38,47,46,91,249,6,132,14,0,31,255,228,64,98,192,64,5,191,10,139,20,87, -105,130,76,156,197,132,4,0,38,187,27,187,85,81,104,28,201,204,160,31,243, -23,33,127,125,28,247,193,102,79,142,202,44,3,255,113,84,118,82,184,47,232, -52,201,241,216,176,139,0,255,111,45,236,84,155,148,58,5,66,76,4,0,146,31, -181,68,66,209,136,61,58,52,170,49,190,202,1,255,53,4,243,51,249,222,108,22, -157,26,85,25,64,63,246,160,158,102,127,59,205,74,7,135,23,53,2,65,48,227, -223,205,64,160,0,48,76,60,244,238,80,40,0,20,19,15,76,59,148,10,0,7,5,195, -211,14,230,74,72,130,99,203,167,98,129,64,1,32,120,247,243,80,40,0,44,15, -47,142,10,5,0,6,130,230,217,191,127,37,2,128,3,192,246,111,206,160,80,0, -136,30,220,62,19,151,160,123,116,238,79,94,129,240,223,221,73,32,0,48,110, -88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0, -21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23, -134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0, -191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,32,98,65,64,0,0,0, -0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60, -56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7, -147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3, -252,131,32,248,250,242,229,151,119,72,240,3,254,148,0,2,168,254,0,0,255, -167,0,33,68,88,32,0,33,64,176,2,170,254,0,0,255,169,0,33,69,220,32,0,33,67, -184,2,172,254,0,0,255,171,8,137,144,128,0,0,0,0,0,0,0,68,73,4,195,187,126, -226,8,4,178,16,41,164,32,147,7,136,52,193,240,0,18,17,48,124,0,8,133,76,31, -0,3,33,147,7,192,1,8,116,193,240,0,82,127,255,132,47,65,11,137,191,174,45, -153,98,242,229,191,144,105,4,95,47,46,91,249,32,211,185,6,94,92,183,242,65, -163,14,236,155,52,238,206,0,85,255,192,6,13,167,157,109,57,123,136,144,31, -245,192,3,5,231,179,78,60,163,9,0,2,10,199,248,0,3,254,192,4,32,249,242, -244,147,187,163,129,116,128,24,66,51,229,233,87,78,238,142,5,210,0,65,8, -207,151,164,157,221,24,182,23,72,1,140,39,62,94,149,116,238,232,197,176, -186,64,8,97,25,242,244,147,187,163,54,66,233,0,50,132,231,203,210,174,157, -221,25,178,23,72,1,20,43,62,94,145,182,111,195,209,155,33,116,128,17,194, -179,229,233,27,102,252,61,27,52,23,72,1,36,31,158,94,146,119,116,112,50, -208,3,8,71,60,189,42,233,221,209,192,203,64,8,33,28,242,244,147,187,163,22, -195,45,0,49,132,243,203,210,174,157,221,24,182,25,104,1,12,35,158,94,146, -119,116,102,200,101,160,6,80,158,121,122,85,211,187,163,54,67,45,0,34,133, -115,203,210,54,205,248,122,51,100,50,208,2,56,87,60,189,35,108,223,135,163, -102,131,45,0,36,7,255,248,1,11,50,136,132,115,235,139,15,46,88,124,140,36, -0,4,43,79,224,139,16,15,252,0,0,0,0,0,0,0,101,253,152,0,5,109,252,17,98,1, -255,128,0,0,0,0,0,0,12,191,181,0,0,174,63,130,44,64,63,240,0,0,0,0,0,0,1, -151,246,224,0,21,215,240,69,136,8,0,0,0,0,0,0,0,0,50,254,228,0,2,188,254,8, -177,1,0,0,0,0,0,0,0,0,6,95,221,128,0,87,223,193,22,32,32,8,0,0,0,0,0,0,0, -203,251,208,0,11,3,248,34,196,4,1,0,0,0,0,0,0,0,25,127,126,0,1,97,127,4,88, -128,128,32,0,0,0,0,0,0,3,47,240,64,0,44,79,224,139,16,16,8,0,0,0,0,0,0,0, -101,254,24,0,5,141,252,1,96,216,247,238,199,135,162,162,33,90,121,197,221, -143,126,77,59,179,172,146,17,167,156,46,185,179,101,228,176,65,89,77,16, -124,123,246,240,195,203,40,162,64,0,193,255,138,5,144,158,89,112,228,171, -39,119,71,2,232,132,114,203,135,36,157,221,28,11,164,0,66,25,203,46,28,149, -100,238,232,197,180,200,162,233,0,1,134,114,203,135,37,89,59,186,49,109,10, -40,186,64,2,97,124,178,225,201,39,119,70,45,166,69,23,72,0,140,47,150,92, -57,36,238,232,197,180,40,162,233,0,25,134,114,203,135,37,89,59,186,51,101, -50,40,186,64,0,161,156,178,225,201,86,78,238,140,217,66,138,46,144,0,168, -95,44,184,114,73,221,209,155,41,145,69,210,0,37,11,229,151,14,73,59,186,51, -101,10,40,186,64,6,161,124,178,225,201,27,102,252,61,38,69,23,72,0,28,47, -150,92,57,35,108,223,135,164,40,162,233,0,11,134,114,203,135,36,77,253,113, -108,203,50,40,186,64,1,33,156,178,225,201,19,127,92,91,50,194,138,46,144,0, -200,87,44,184,114,85,147,187,164,200,162,237,0,5,133,114,203,135,37,89,59, -186,66,138,46,208,0,216,79,44,184,114,73,221,210,100,81,118,128,10,194,121, -101,195,146,78,238,144,162,139,180,0,118,21,223,150,158,153,106,201,221, -209,192,203,33,61,249,105,233,150,78,238,142,6,90,0,33,13,239,203,79,76, -181,100,238,232,197,180,200,163,45,0,1,134,247,229,167,166,90,178,119,116, -98,218,20,81,150,128,4,195,59,242,211,211,44,157,221,24,182,153,20,101,160, -2,48,206,252,180,244,203,39,119,70,45,161,69,25,104,0,204,55,191,45,61,50, -213,147,187,163,54,83,34,140,180,0,10,27,223,150,158,153,106,201,221,209, -155,40,81,70,90,0,21,12,239,203,79,76,178,119,116,102,202,100,81,150,128,9, -67,59,242,211,211,44,157,221,25,178,133,20,101,160,3,80,206,252,180,244, -203,27,102,252,61,38,69,25,104,0,28,51,191,45,61,50,198,217,191,15,72,81, -70,90,0,23,13,239,203,79,76,177,55,245,197,179,44,200,163,45,0,4,134,247, -229,167,166,88,155,250,226,217,150,20,81,150,128,6,66,251,242,211,211,45, -89,59,186,76,138,51,16,0,88,95,126,90,122,101,171,39,119,72,81,70,98,0,27, -10,239,203,79,76,178,119,116,153,20,102,32,2,176,174,252,180,244,203,39, -119,72,81,70,98,0,58,40,173,176,82,90,4,19,54,157,155,21,217,6,203,199,174, -29,156,197,9,7,199,191,111,12,60,178,138,20,0,6,9,143,127,15,42,208,130, -243,217,167,30,81,132,65,123,242,211,211,42,228,0, +DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { +144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, +124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, +167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, +64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, +142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, +242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, +1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, +33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, +198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, +10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, +53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, +84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, +109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, +11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, +145,153,136,15,255,0,0,0,0,0,0,25,152,15,254,0,0,0,0,0,0,25,120,144,13,96, +155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168,23, +194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, +137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, +36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, +209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, +144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, +122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, +136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, +195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, +17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, +2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, +35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, +174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, +0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, +73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, +89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, +144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, +78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, +196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, +132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, +162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, +160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, +151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, +113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, +129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, +168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, +153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, +36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, +203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, +136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, +207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, +37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, +44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, +231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, +30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, +196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, +35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, +34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, +120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, +68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, +228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, +18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, +249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, +102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, +9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, +255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, +57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, +130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, +11,161,32,63,247,255,255,255,255,255,255,137,235,16,221,170,129,116,36,0,0, +0,0,0,0,0,0,28,196,7,255,128,0,0,0,0,0,2,61,123,164,137,162,164,218,67,74, +134,162,120,128,255,224,0,0,0,0,0,0,71,173,33,129,52,84,155,72,105,80,212, +79,16,63,252,0,0,0,0,0,0,3,244,143,146,22,230,192,60,176,0,0,0,0,0,0,33, +214,2,251,82,1,73,180,134,204,134,36,96,33,159,255,255,255,255,255,255,144, +235,16,221,169,0,164,218,67,102,67,18,48,48,207,255,255,255,255,255,255, +196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34,92, +42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, +194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, +0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, +18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, +5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,127, +248,0,0,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, +185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, +130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, +197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, +12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, +218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, +98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, +250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, +40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, +49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, +56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, +173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, +168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, +18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, +210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, +72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, +72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, +186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, +50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, +74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, +186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, +162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, +55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, +48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, +82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, +144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, +221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, +105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, +137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, +23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, +19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, +72,115,96,32,106,15,0,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0, +192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, +195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, +1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, +36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, +0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, +102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, +20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, +216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, +81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, +166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, +20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, +68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, +159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, +67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, +173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, +140,35,103,0,255,192,0,0,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29, +162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,25,244,35,208,226,100,150,211, +201,29,162,44,140,35,103,0,255,192,0,0,0,0,0,0,206,26,4,35,208,226,100,150, +211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,20,35,208,226,100, +150,211,201,29,162,44,140,35,103,1,0,0,0,0,0,0,0,0,206,26,36,35,208,226, +100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,52,35,208, +226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,68,35, +208,226,100,150,211,201,29,162,44,140,35,103,1,0,64,0,0,0,0,0,0,206,26,84, +35,208,226,100,150,211,201,29,162,44,140,35,103,1,0,128,0,0,0,0,0,0,195, +154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, +25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, +235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, +196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, +200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, +213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, +183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, +221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, +31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, +18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, +59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, +39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, +73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, +61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, +67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, +167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, +68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, +193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, +178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, +157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, +81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, +133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, +22,209,68,201,187,129,4,2,8,3,132,64,60,36,4,0,91,240,168,177,69,118,144, +157,91,116,116,32,32,1,53,216,221,218,170,139,3,234,219,165,0,255,152,185, +11,251,232,231,188,47,86,227,105,18,1,255,184,170,59,41,92,23,240,110,173, +198,209,208,36,3,253,188,183,177,82,110,80,224,93,122,32,32,4,144,253,170, +34,22,140,7,236,161,25,232,237,105,64,63,230,160,158,102,127,59,205,11,217, +66,51,210,128,127,237,65,60,204,254,119,155,171,197,34,168,48,6,90,194,1,0, +39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6, +51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0, +201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69, +234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165, +1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107, +64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132, +103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145, +52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104, +146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18, +52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129, +6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155, +163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17, +46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75, +204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9, +113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178, +66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38, +232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38, +3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202, +160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0, +179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181, +192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121, +35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,25,188,56,132,122,28,76,146, +218,121,35,180,69,145,132,108,224,31,248,0,0,0,0,0,0,40,160,45,110,23,30, +176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153, +37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0,51,120,145,8,244,56, +153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,161,8,244, +56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0,51,120,177,8, +244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120,193, +8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51,120, +209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0,0,0,51, +120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,32,0,0,0,0,0,0, +32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137, +112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34, +74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8, +35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117, +96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32, +148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2, +3,107,173,218,3,192, }; #elif defined(DUK_USE_DOUBLE_ME) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3833] = { -105,195,75,32,3,148,52,154,248,9,26,13,128,112,105,0,240,22,20,26,95,124,6, -152,52,137,0,120,99,74,239,129,18,70,241,191,2,98,13,79,32,42,88,210,90,2, -240,1,50,141,37,168,76,94,216,118,69,229,203,127,44,0,84,163,73,106,21,75, -14,236,249,98,242,229,191,150,0,46,81,164,181,14,165,151,54,94,89,119,99, -203,23,151,45,252,176,1,146,141,37,168,93,63,59,186,97,241,23,151,45,252, -176,1,178,141,37,168,77,79,60,50,197,229,203,127,44,0,116,163,73,106,17,86, -148,152,188,185,111,229,128,15,148,129,198,137,36,58,166,142,91,251,212, -243,195,44,94,92,183,242,13,79,8,45,14,91,252,121,148,52,199,120,63,72,105, -21,240,118,128,210,237,224,245,17,165,43,224,211,55,231,207,151,148,161,70, -145,0,31,40,107,26,2,18,138,26,228,192,142,0,16,161,174,76,9,74,26,228,192, -158,0,8,161,174,76,10,96,2,42,26,228,192,174,0,26,161,174,76,11,96,3,74,26, -228,192,190,0,44,161,174,76,12,96,3,202,26,228,192,206,0,70,161,169,84,14, -202,3,255,254,32,234,0,0,7,195,248,0,0,0,0,119,0,0,3,193,252,0,0,0,0,57, -136,1,152,32,16,194,0,166,24,6,49,0,57,138,2,12,96,18,99,128,163,32,5,153, -40,76,94,216,118,69,229,203,127,35,41,10,165,135,118,124,177,121,114,223, -200,203,67,169,101,205,151,150,93,216,242,197,229,203,127,35,49,11,167,231, -119,76,62,34,242,229,191,145,154,132,212,243,195,44,94,92,183,242,51,144, -138,180,164,197,229,203,127,35,60,6,26,0,52,208,193,226,117,215,211,15,12, -166,146,11,67,150,255,30,77,24,58,113,64,243,92,8,27,0,68,217,130,70,212, -19,54,224,161,185,5,77,216,44,111,65,115,126,12,28,16,100,225,156,16,32,18, -17,195,15,46,121,100,238,232,136,136,87,12,60,185,229,141,179,126,30,136, -100,130,233,231,59,12,228,34,66,52,243,141,167,118,158,153,80,73,9,201,151, -30,252,153,106,210,146,118,72,150,76,184,247,228,203,86,148,152,123,246, -240,223,187,46,238,135,132,132,229,221,143,126,76,181,105,73,61,36,75,46, -236,123,242,101,171,74,76,61,251,120,111,221,151,119,67,226,65,178,243,199, -135,134,83,242,66,58,238,203,207,30,30,25,81,201,5,225,203,78,238,136,163, -208,92,59,50,242,232,138,62,0,2,38,163,19,255,255,224,142,80,192,0,20,31, -240,14,135,103,203,210,135,45,253,55,244,243,195,44,252,205,197,0,1,18,221, -82,0,3,24,207,151,164,254,251,168,114,223,195,47,46,158,98,101,231,143,150, -158,29,55,242,104,68,79,62,94,147,251,238,161,203,127,12,188,186,121,157, -135,110,94,109,100,131,99,229,151,15,76,172,168,8,89,217,16,201,151,54,157, -217,104,114,223,195,47,46,154,114,243,102,68,19,158,92,59,27,73,6,205,203, -46,95,89,91,74,0,3,17,225,203,47,108,187,186,69,241,211,46,238,122,119,238, -230,216,72,70,158,116,242,225,217,151,35,81,33,26,121,198,229,191,214,93, -205,69,0,1,134,105,231,23,199,76,187,185,233,197,179,43,73,32,154,242,249, -230,214,80,0,31,255,193,2,38,103,110,117,24,81,115,0,78,228,0,161,208,16, -237,24,121,207,239,186,135,45,252,50,242,233,229,188,144,221,60,232,114, -223,211,127,79,60,50,207,204,224,72,167,14,91,248,101,229,211,204,158,113, -119,117,219,151,150,28,91,50,184,144,40,95,224,0,15,248,64,4,20,78,129,5, -195,195,134,207,38,232,130,99,195,179,97,201,244,19,22,157,217,14,15,130, -135,254,0,48,125,60,224,242,229,135,200,9,1,255,12,2,162,136,112,2,112,80, -128,0,193,177,239,221,143,15,64,35,224,152,20,144,62,27,248,3,2,9,195,175, -61,0,231,208,126,89,123,101,229,207,40,72,32,188,244,105,205,208,40,16,94, -123,52,227,202,22,136,39,61,252,186,6,18,13,207,134,205,56,242,134,175,65, -250,238,231,163,78,110,129,231,208,125,59,178,101,241,63,48,25,248,0,12,47, -102,30,125,36,238,201,151,196,252,192,103,255,255,240,92,189,178,242,242,8, -105,4,231,191,110,80,67,80,0,24,62,109,252,162,225,199,160,16,212,0,10,7, -183,15,0,67,80,0,56,54,109,59,58,101,228,8,106,0,9,6,229,151,39,92,121,66, -15,192,0,97,124,178,228,235,143,45,45,57,244,116,8,63,255,255,10,39,248,0, -195,51,114,223,182,30,140,60,161,239,201,149,248,248,31,241,0,140,80,129, -202,10,49,128,10,35,1,6,199,163,15,40,61,32,9,10,199,163,15,40,123,242,101, -131,210,4,144,108,123,247,99,195,210,8,250,15,167,118,76,190,39,230,131,52, -133,236,195,207,164,157,217,50,248,159,154,12,212,0,6,27,179,126,60,59,50, -195,223,183,134,30,89,97,9,5,219,135,166,61,16,164,131,242,203,195,102,28, -121,97,145,6,231,151,15,44,122,33,201,5,231,179,78,60,177,8,130,243,225, -179,79,72,148,66,121,245,197,207,167,45,59,179,197,162,23,211,124,205,253, -242,242,135,135,158,87,240,68,122,111,153,191,30,29,153,102,111,239,151, -148,60,60,242,191,130,23,211,125,94,28,50,242,135,135,158,87,240,128,0,196, -122,111,153,191,30,29,153,106,240,225,151,148,60,60,242,191,132,0,6,9,211, -150,157,177,160,131,115,235,139,159,78,81,72,10,47,248,0,3,254,40,17,138, -48,66,136,152,64,0,66,129,48,5,27,252,88,76,216,54,47,214,131,50,172,88,31, -255,253,239,255,255,255,255,240,153,178,103,95,173,6,101,88,176,0,0,0,0,0, -64,0,0,3,168,0,0,31,15,224,0,0,0,17,26,19,233,201,169,38,180,91,242,103,70, -147,58,77,75,48,0,0,60,31,192,0,0,0,34,51,162,199,131,82,77,104,183,228, -206,141,38,116,154,150,96,0,0,120,127,128,0,0,0,0,15,248,192,70,40,0,0,0,0, -0,0,0,0,3,10,44,68,9,216,8,20,49,130,15,211,124,109,62,50,228,95,36,55,166, -248,190,56,111,221,151,119,77,56,118,47,18,23,211,125,14,89,113,233,231, -167,126,230,18,5,31,252,0,224,188,48,242,231,148,116,144,58,181,33,143,127, -64,247,111,238,56,0,127,199,2,49,72,0,0,248,127,0,0,0,0,180,81,36,4,51,166, -248,152,122,101,167,211,150,157,217,201,2,0,3,12,233,190,166,157,185,105, -244,229,167,118,114,64,128,1,4,228,129,0,3,137,116,223,51,126,60,59,50,196, -195,211,45,62,156,180,238,206,72,16,0,72,151,77,243,55,227,195,179,45,77, -59,114,211,233,203,78,236,228,129,0,5,10,73,2,0,12,21,18,4,0,28,82,35,32, -80,74,8,62,124,189,42,105,219,148,148,16,188,249,122,70,235,179,101,156, -184,121,15,132,0,34,29,159,47,74,181,33,198,235,179,101,156,184,121,15,132, -0,38,17,159,47,73,187,247,116,208,62,16,0,168,94,124,189,42,212,135,55,126, -238,154,7,194,0,23,7,207,151,164,76,61,50,143,132,0,50,21,159,47,74,181,33, -196,195,211,40,248,64,3,96,217,242,244,137,135,200,248,64,3,161,57,242,244, -171,82,28,76,62,71,194,0,31,8,207,151,164,141,253,121,115,31,8,0,132,47,62, -94,149,106,67,145,191,175,46,99,225,0,17,133,103,203,210,110,157,221,122, -101,230,62,16,1,40,110,124,189,42,212,135,55,78,238,189,50,243,31,8,0,156, -43,62,94,148,242,227,223,187,39,49,240,128,10,67,115,229,233,86,164,58,121, -113,239,221,147,152,248,64,5,97,249,242,244,155,167,102,205,60,242,227,223, -187,39,49,240,128,11,68,179,229,233,86,164,57,186,118,108,211,207,46,61, -251,178,115,31,8,0,188,71,62,94,149,52,237,203,235,126,236,179,243,102,231, -151,161,0,32,252,242,244,169,167,110,82,34,67,249,229,233,55,78,205,154, -121,229,199,191,118,78,100,37,0,24,137,115,203,210,173,72,115,116,236,217, -167,158,92,123,247,100,230,66,80,1,152,87,60,189,41,229,199,191,118,78,100, -43,224,3,80,222,121,122,85,169,14,158,92,123,247,100,230,66,190,0,55,10, -231,151,164,221,59,186,244,203,204,133,252,0,114,27,207,47,74,181,33,205, -211,187,175,76,188,200,95,192,7,97,28,242,244,145,191,175,46,100,51,224,3, -208,190,121,122,85,169,14,70,254,188,185,144,207,128,15,193,249,229,233,19, -15,76,164,37,0,32,133,115,203,210,173,72,113,48,244,202,66,80,2,24,71,60, -189,38,239,221,211,65,10,248,1,20,47,158,94,149,106,67,155,191,119,77,4,43, -224,4,112,190,121,122,70,235,179,101,156,184,121,16,191,128,18,67,185,229, -233,86,164,56,221,118,108,179,151,15,34,23,240,2,88,62,124,189,44,229,195, -200,124,32,4,208,126,121,122,89,203,135,145,9,64,9,194,145,254,0,0,255,144, -24,100,130,14,0,16,176,2,192,129,11,33,12,1,168,193,108,96,186,48,95,32,0, -0,0,0,0,0,0,0,56,38,95,25,113,189,18,9,211,47,62,143,100,20,95,0,20,159, -240,0,7,252,144,162,241,2,195,66,7,11,89,204,140,197,252,229,197,226,230, -115,3,16,69,19,64,5,43,252,0,9,255,40,16,188,33,49,123,97,217,23,151,45, -252,131,66,7,0,20,191,240,0,39,252,176,66,240,133,82,195,187,62,88,188,185, -111,228,26,16,56,0,166,127,128,1,63,230,2,23,132,58,150,92,217,121,101,221, -143,44,94,92,183,242,13,8,28,0,83,127,192,0,159,243,65,11,194,23,79,206, -238,152,124,69,229,203,127,32,208,129,192,5,59,252,0,9,255,56,16,188,33,53, -60,240,203,23,151,45,252,131,66,7,0,20,255,240,0,39,252,240,66,240,132,85, -165,38,47,46,91,249,6,132,14,0,31,255,228,64,98,192,10,191,5,64,105,87,20, -139,130,76,156,197,132,11,22,176,36,1,101,91,91,184,28,201,204,160,33,23, -115,31,247,156,253,127,65,102,79,142,202,44,4,113,95,115,255,232,34,182,88, -52,201,241,216,176,139,1,239,47,108,252,59,148,152,86,5,66,76,15,178,16, -148,1,130,212,69,72,61,58,52,170,49,190,202,4,245,7,49,254,105,219,251,52, -22,157,26,85,25,64,158,160,246,63,205,59,127,102,74,7,135,23,53,2,65,48, -227,223,205,64,160,0,48,76,60,244,238,80,40,0,20,19,15,76,59,148,10,0,7,5, -195,211,14,230,74,72,130,99,203,167,98,129,64,1,32,120,247,243,80,40,0,44, -15,47,142,10,5,0,6,130,230,217,191,127,37,2,128,3,192,246,111,206,160,80,0, -136,30,220,62,19,151,160,123,116,238,79,94,129,240,223,221,73,32,0,48,110, -88,119,100,223,181,68,16,94,91,250,238,200,160,80,0,152,31,61,59,148,10,0, -21,4,231,199,151,69,2,128,5,192,250,97,220,160,80,0,192,127,255,128,20,23, -134,30,92,242,164,34,19,207,167,45,59,179,233,205,229,37,129,127,255,0,0, -191,255,128,0,63,255,197,131,246,203,203,158,157,251,160,64,65,98,32,0,0,0, -0,3,166,156,30,53,32,249,165,131,76,223,159,62,94,70,172,114,16,176,144,60, -56,250,19,18,5,159,25,89,32,121,180,238,42,30,129,229,221,140,164,122,7, -147,46,50,129,232,62,61,251,120,97,199,208,156,129,83,127,0,50,250,69,3, -252,131,32,248,250,242,229,151,119,72,240,3,254,148,0,2,168,254,0,0,255, -167,0,33,68,88,32,0,33,64,176,2,170,254,0,0,255,169,0,33,69,220,32,0,33,67, -184,2,172,254,0,0,255,171,8,137,144,0,0,0,128,0,0,0,0,68,73,4,195,187,126, -226,8,4,178,16,41,164,32,147,7,136,52,193,240,0,18,17,48,124,0,8,133,76,31, -0,3,33,147,7,192,1,8,116,193,240,0,82,127,255,132,47,65,11,137,191,174,45, -153,98,242,229,191,144,105,4,95,47,46,91,249,32,211,185,6,94,92,183,242,65, -163,14,236,155,52,238,206,0,85,255,192,6,13,167,157,109,57,123,136,144,31, -245,192,3,5,231,179,78,60,163,9,0,2,10,199,248,0,3,254,192,4,32,249,242, -244,147,187,163,129,116,128,24,66,51,229,233,87,78,238,142,5,210,0,65,8, -207,151,164,157,221,24,182,23,72,1,140,39,62,94,149,116,238,232,197,176, -186,64,8,97,25,242,244,147,187,163,54,66,233,0,50,132,231,203,210,174,157, -221,25,178,23,72,1,20,43,62,94,145,182,111,195,209,155,33,116,128,17,194, -179,229,233,27,102,252,61,27,52,23,72,1,36,31,158,94,146,119,116,112,50, -208,3,8,71,60,189,42,233,221,209,192,203,64,8,33,28,242,244,147,187,163,22, -195,45,0,49,132,243,203,210,174,157,221,24,182,25,104,1,12,35,158,94,146, -119,116,102,200,101,160,6,80,158,121,122,85,211,187,163,54,67,45,0,34,133, -115,203,210,54,205,248,122,51,100,50,208,2,56,87,60,189,35,108,223,135,163, -102,131,45,0,36,7,255,248,1,11,50,136,132,115,235,139,15,46,88,124,140,36, -0,4,43,79,224,139,16,0,0,60,15,192,0,0,0,0,101,253,152,0,5,109,252,17,98,0, -0,7,129,248,0,0,0,0,12,191,181,0,0,174,63,130,44,64,0,0,240,63,0,0,0,0,1, -151,246,224,0,21,215,240,69,136,0,0,0,8,0,0,0,0,0,50,254,228,0,2,188,254,8, -177,0,0,0,1,0,0,0,0,0,6,95,221,128,0,87,223,193,22,32,0,0,8,32,0,0,0,0,0, -203,251,208,0,11,3,248,34,196,0,0,1,4,0,0,0,0,0,25,127,126,0,1,97,127,4,88, -128,0,0,32,128,0,0,0,0,3,47,240,64,0,44,79,224,139,16,0,0,8,16,0,0,0,0,0, -101,254,24,0,5,141,252,1,96,216,247,238,199,135,162,162,33,90,121,197,221, -143,126,77,59,179,172,146,17,167,156,46,185,179,101,228,176,65,89,77,16, -124,123,246,240,195,203,40,162,64,0,193,255,138,5,144,158,89,112,228,171, -39,119,71,2,232,132,114,203,135,36,157,221,28,11,164,0,66,25,203,46,28,149, -100,238,232,197,180,200,162,233,0,1,134,114,203,135,37,89,59,186,49,109,10, -40,186,64,2,97,124,178,225,201,39,119,70,45,166,69,23,72,0,140,47,150,92, -57,36,238,232,197,180,40,162,233,0,25,134,114,203,135,37,89,59,186,51,101, -50,40,186,64,0,161,156,178,225,201,86,78,238,140,217,66,138,46,144,0,168, -95,44,184,114,73,221,209,155,41,145,69,210,0,37,11,229,151,14,73,59,186,51, -101,10,40,186,64,6,161,124,178,225,201,27,102,252,61,38,69,23,72,0,28,47, -150,92,57,35,108,223,135,164,40,162,233,0,11,134,114,203,135,36,77,253,113, -108,203,50,40,186,64,1,33,156,178,225,201,19,127,92,91,50,194,138,46,144,0, -200,87,44,184,114,85,147,187,164,200,162,237,0,5,133,114,203,135,37,89,59, -186,66,138,46,208,0,216,79,44,184,114,73,221,210,100,81,118,128,10,194,121, -101,195,146,78,238,144,162,139,180,0,118,21,223,150,158,153,106,201,221, -209,192,203,33,61,249,105,233,150,78,238,142,6,90,0,33,13,239,203,79,76, -181,100,238,232,197,180,200,163,45,0,1,134,247,229,167,166,90,178,119,116, -98,218,20,81,150,128,4,195,59,242,211,211,44,157,221,24,182,153,20,101,160, -2,48,206,252,180,244,203,39,119,70,45,161,69,25,104,0,204,55,191,45,61,50, -213,147,187,163,54,83,34,140,180,0,10,27,223,150,158,153,106,201,221,209, -155,40,81,70,90,0,21,12,239,203,79,76,178,119,116,102,202,100,81,150,128,9, -67,59,242,211,211,44,157,221,25,178,133,20,101,160,3,80,206,252,180,244, -203,27,102,252,61,38,69,25,104,0,28,51,191,45,61,50,198,217,191,15,72,81, -70,90,0,23,13,239,203,79,76,177,55,245,197,179,44,200,163,45,0,4,134,247, -229,167,166,88,155,250,226,217,150,20,81,150,128,6,66,251,242,211,211,45, -89,59,186,76,138,51,16,0,88,95,126,90,122,101,171,39,119,72,81,70,98,0,27, -10,239,203,79,76,178,119,116,153,20,102,32,2,176,174,252,180,244,203,39, -119,72,81,70,98,0,58,40,173,176,82,90,4,19,54,157,155,21,217,6,203,199,174, -29,156,197,9,7,199,191,111,12,60,178,138,20,0,6,9,143,127,15,42,208,130, -243,217,167,30,81,132,65,123,242,211,211,42,228,0, +DUK_INTERNAL const duk_uint8_t duk_builtins_data[4281] = { +144,148,105,226,32,68,52,228,254,12,104,202,37,132,52,167,194,138,105,245, +124,57,28,211,57,18,64,52,239,126,44,138,111,175,241,164,19,87,145,30,33, +167,22,145,159,8,211,139,9,225,42,5,240,145,139,163,163,8,211,139,10,228, +64,211,19,132,140,93,29,56,70,156,88,119,34,66,146,36,104,137,194,70,46, +142,172,35,78,44,47,146,195,102,11,240,145,139,163,175,8,211,139,9,228,240, +242,112,145,139,163,179,8,211,139,8,237,34,130,118,49,116,118,225,26,48,0, +1,98,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132, +33,8,66,26,180,105,97,167,68,150,34,33,154,112,0,1,91,247,35,79,111,237, +198,174,232,47,31,23,95,17,13,31,249,96,211,49,50,53,214,77,141,24,0,0,181, +10,228,240,242,15,128,140,65,128,134,188,0,0,90,167,97,181,224,0,2,213,62, +53,224,0,2,213,66,237,120,0,0,181,81,204,107,192,0,5,170,150,67,94,0,0,45, +84,245,90,240,0,1,106,169,162,215,128,0,11,85,93,150,188,0,0,90,171,111,53, +109,22,162,26,48,0,1,84,23,201,146,243,225,26,39,12,145,136,104,192,0,5,61, +11,228,201,121,240,100,19,134,72,196,33,195,14,40,203,112,64,190,76,232, +145,153,136,0,0,31,15,224,0,0,0,25,152,0,0,30,15,224,0,0,0,25,120,144,13, +96,155,194,56,80,206,36,67,141,20,228,70,57,81,206,100,131,156,39,132,168, +23,194,70,46,137,208,21,200,129,166,39,9,24,186,39,72,119,34,66,146,36,104, +137,194,70,46,137,212,23,201,97,179,5,248,72,197,209,58,194,121,60,60,156, +36,98,232,157,129,29,164,80,78,198,46,137,218,146,121,25,71,146,9,209,5, +209,61,48,126,14,138,152,30,67,186,23,143,139,175,131,202,135,228,72,85, +144,83,60,179,30,94,209,233,102,30,98,105,230,103,30,114,121,231,104,30, +122,137,231,233,30,130,153,232,106,30,138,169,232,235,30,144,67,193,25,19, +136,108,207,30,41,224,140,137,194,173,192,153,228,5,242,100,188,248,70,137, +195,36,79,78,47,147,37,231,193,144,78,25,34,122,145,111,36,74,232,176,13, +17,61,234,226,93,207,148,160,84,75,141,7,27,161,32,33,18,225,80,212,76,154, +2,2,70,65,56,100,237,34,140,209,2,67,32,156,50,118,145,64,186,230,61,205, +35,103,155,32,36,141,19,134,78,210,40,206,16,36,70,137,195,39,105,20,11, +174,99,220,210,54,121,210,1,137,33,1,228,207,16,17,70,146,66,3,201,164,32, +0,65,112,152,56,196,159,31,23,77,211,195,201,199,23,160,72,214,246,81,6,12, +73,241,214,111,31,23,60,145,158,56,50,72,81,67,230,232,242,80,19,49,39,199, +89,188,124,92,242,70,120,227,64,194,75,154,72,12,9,73,6,111,21,120,12,40, +144,19,39,25,0,225,144,168,105,56,248,185,228,140,241,200,96,64,100,42,26, +78,62,46,121,35,52,18,92,116,1,36,64,47,158,64,49,98,66,100,156,242,65,23, +196,149,35,103,194,94,100,108,144,230,203,156,64,66,37,201,16,11,32,249, +132,4,34,92,44,93,146,55,152,72,24,137,112,151,153,27,36,5,100,229,144,8, +162,98,92,210,5,76,73,241,214,111,31,23,60,145,158,57,44,48,46,92,185,164, +160,72,151,41,0,50,107,179,244,59,36,93,127,92,6,19,172,3,11,216,0,56,224, +151,29,102,241,241,115,201,25,227,164,64,106,37,199,197,211,116,240,242, +113,197,233,144,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,84, +129,13,173,161,144,168,105,56,98,78,100,142,214,215,69,1,13,173,161,144, +168,105,57,34,78,100,142,214,215,69,16,67,107,105,110,114,168,254,24,147, +153,35,181,181,212,32,67,107,105,110,114,168,254,72,147,153,35,181,181,212, +36,65,130,3,144,8,26,252,200,13,30,85,16,16,64,90,242,231,192,64,161,163, +203,31,26,172,193,17,4,23,105,159,96,27,172,251,16,32,196,4,14,137,112,17, +136,48,164,28,134,80,215,202,1,132,130,8,12,39,52,64,155,31,24,56,36,1,189, +207,132,0,35,233,35,195,62,3,196,149,36,100,72,160,2,200,232,44,227,0,11, +37,160,68,142,128,36,157,25,200,32,26,79,90,4,73,43,192,122,54,71,65,103, +44,248,14,134,140,151,227,138,231,208,45,96,148,248,134,140,151,227,138, +231,240,1,255,254,10,74,146,56,128,104,4,147,152,72,6,144,28,174,143,8,1, +30,1,165,3,96,31,0,211,3,21,11,153,35,0,211,131,68,131,160,137,16,250,5, +196,131,160,137,200,160,199,156,67,248,0,255,255,65,140,10,48,177,115,56, +35,130,60,19,134,79,89,240,52,177,115,56,39,12,156,123,144,217,251,15,135, +34,167,30,20,170,154,255,232,12,47,244,0,97,28,17,224,39,238,32,40,71,4, +120,39,12,156,4,253,228,5,137,195,39,30,228,54,124,4,253,228,128,194,115, +68,9,252,15,128,232,104,201,126,56,191,35,64,90,193,41,241,13,25,47,199,23, +228,105,3,86,225,1,100,224,156,199,130,36,249,144,10,192,76,71,250,16,15, +18,61,96,17,62,200,3,72,128,136,143,247,32,22,75,64,137,248,64,22,79,90,39, +249,64,38,84,12,167,20,52,223,196,2,230,238,45,214,36,120,32,72,158,208,4, +102,238,45,194,2,201,197,186,196,143,4,9,19,218,0,92,221,202,61,228,143,4, +9,19,218,8,35,55,113,110,16,22,78,81,239,36,120,32,72,158,208,64,73,197,12, +255,0,13,18,60,128,159,212,128,169,76,17,156,185,100,76,255,163,64,65,26, +57,114,200,153,255,70,144,33,13,18,232,50,75,226,104,6,149,3,41,199,246, +130,12,128,28,142,156,120,203,175,158,8,194,207,1,6,81,20,79,88,11,237,84, +11,161,32,127,255,247,191,255,255,255,255,137,235,16,221,170,129,116,36,0, +0,0,0,0,16,0,0,12,196,0,0,15,135,240,0,0,0,2,61,123,164,137,162,164,218,67, +74,134,162,120,128,0,1,224,254,0,0,0,0,71,173,33,129,52,84,155,72,105,80, +212,79,16,0,0,60,63,192,0,0,0,3,244,143,146,22,230,192,0,0,176,60,0,0,0,0, +33,214,2,251,82,1,73,180,134,204,134,36,96,127,255,159,161,255,255,255,255, +144,235,16,221,169,0,164,218,67,102,67,18,48,63,255,207,240,255,255,255, +255,196,60,17,145,56,134,204,241,226,158,8,200,156,42,220,9,158,65,196,34, +92,42,26,137,147,120,64,74,37,196,54,100,49,35,188,36,5,68,184,208,113,187, +194,80,212,75,146,1,73,196,54,100,49,35,188,38,57,37,56,240,0,0,0,0,0,0,0, +0,32,235,248,68,48,156,2,24,94,24,0,243,119,10,139,144,123,242,3,102,238, +18,239,115,72,217,160,11,223,16,23,55,113,241,32,145,36,57,188,18,16,102,3, +5,120,35,34,89,32,15,180,152,173,127,0,218,235,88,0,228,180,227,200,0,0, +248,127,0,0,0,0,197,107,240,64,6,77,220,24,38,78,74,113,67,77,130,4,12,155, +185,52,48,156,148,226,134,155,4,10,194,96,129,132,166,238,45,194,2,201,193, +130,100,228,167,20,52,216,32,113,41,187,139,112,128,178,114,104,97,57,41, +197,13,54,8,32,48,216,32,130,195,224,130,19,97,124,134,23,6,0,57,137,62,77, +12,38,12,0,179,18,124,45,22,190,96,128,141,176,134,28,98,79,180,152,139, +218,45,124,193,1,27,97,16,32,196,159,24,230,204,246,194,40,89,137,62,210, +98,103,92,217,158,216,70,7,49,39,193,130,100,182,17,194,140,73,246,147,16, +250,9,146,216,72,6,49,39,193,131,22,194,72,73,137,62,210,98,31,65,139,97, +40,32,196,159,14,234,70,86,194,88,89,137,62,210,98,63,93,72,202,216,76,10, +49,39,198,33,180,153,37,108,38,134,152,147,237,38,38,117,13,164,201,43,97, +56,40,196,159,36,65,57,163,149,176,158,26,98,79,180,152,165,210,9,205,28, +173,133,0,243,18,124,98,22,180,72,130,115,71,43,97,68,72,196,159,105,49,51, +168,90,209,34,9,205,28,173,133,33,19,18,124,154,24,76,185,164,227,138,89, +18,119,0,7,145,39,201,161,132,188,64,124,137,62,49,11,90,36,65,57,163,149, +210,166,37,34,79,180,152,153,212,45,104,145,4,230,142,87,74,160,84,137,62, +72,130,115,71,43,171,234,134,200,147,237,38,41,116,130,115,71,43,171,235,5, +72,147,227,16,218,76,146,186,254,184,108,137,62,210,98,103,80,218,76,146, +186,254,192,68,137,62,29,212,140,174,207,178,23,34,79,180,152,143,215,82, +50,187,62,208,60,137,62,12,19,37,210,182,21,34,79,180,152,135,208,76,151, +74,224,68,137,62,49,205,153,238,175,186,23,34,79,180,152,153,215,54,103, +186,190,240,92,137,62,22,139,95,48,64,70,235,251,225,210,36,251,73,136,189, +162,215,204,16,17,186,255,2,14,98,79,152,32,35,108,48,64,242,36,249,130,2, +55,75,6,212,224,72,200,51,128,114,108,28,100,128,0,0,0,0,0,0,0,12,110,127, +48,98,115,249,201,117,243,249,195,21,159,206,38,47,63,156,86,8,75,144,94, +82,1,38,73,79,208,67,95,233,1,6,128,14,79,129,186,40,249,18,149,182,207, +144,200,155,188,248,204,105,184,207,142,199,137,175,201,0,159,72,10,5,21, +221,10,120,74,129,124,36,98,232,228,74,81,62,160,20,10,107,186,21,114,32, +105,137,194,70,46,142,68,165,19,235,1,64,170,187,161,119,34,66,146,36,104, +137,194,70,46,142,68,165,19,236,1,64,174,187,161,95,37,134,204,23,225,35, +23,71,34,82,137,246,128,160,89,93,208,167,147,195,201,194,70,46,142,68,165, +19,238,1,64,182,187,161,71,105,20,19,177,139,163,145,41,68,16,7,6,15,82,70, +72,115,96,0,15,106,32,0,0,0,0,91,60,165,195,201,194,8,134,149,216,162,0, +192,41,225,8,2,48,177,36,1,149,13,196,15,0,200,209,97,199,128,99,32,176, +195,192,113,57,143,0,167,133,32,230,80,28,202,139,175,238,2,48,189,192,20, +1,119,80,87,193,186,129,89,56,72,197,209,200,193,185,35,23,71,109,13,219, +36,98,232,237,156,13,26,208,211,14,102,19,87,137,91,95,128,0,10,96,24,92,0, +0,83,2,53,56,0,0,165,3,28,204,160,160,226,100,226,200,211,76,241,240,0,1, +102,8,22,75,64,137,73,20,230,105,133,7,19,39,22,70,154,103,143,128,0,11,48, +20,28,76,156,113,75,34,78,62,0,0,45,3,103,31,0,0,22,65,44,57,137,62,33,179, +216,162,152,192,131,18,124,162,27,61,138,41,108,32,196,159,16,217,232,235, +81,76,104,73,137,62,81,13,158,142,181,20,184,16,98,79,136,108,244,244,168, +166,56,36,196,159,40,134,207,79,74,138,93,10,49,39,194,173,192,158,158,149, +20,188,20,98,79,133,91,129,61,109,74,41,124,30,68,159,16,217,236,83,108,96, +68,137,62,81,13,158,197,54,182,17,34,79,136,108,244,117,169,182,52,38,68, +159,40,134,207,71,90,155,92,8,145,39,196,54,122,122,84,219,28,19,34,79,148, +67,103,167,165,77,174,133,72,147,225,86,224,79,79,74,155,94,10,145,39,194, +173,192,158,182,165,54,190,206,25,212,35,208,226,100,150,211,201,29,162,44, +140,35,103,0,0,3,192,252,0,0,0,0,206,25,228,35,208,226,100,150,211,201,29, +162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,25,244,35,208,226,100,150,211, +201,29,162,44,140,35,103,0,0,3,192,252,0,0,0,0,206,26,4,35,208,226,100,150, +211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,20,35,208,226,100, +150,211,201,29,162,44,140,35,103,0,0,0,1,0,0,0,0,0,206,26,36,35,208,226, +100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,52,35,208, +226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,68,35, +208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,65,0,0,0,0,0,206,26,84, +35,208,226,100,150,211,201,29,162,44,140,35,103,0,0,0,129,0,0,0,0,0,195, +154,99,16,38,36,0,251,68,117,179,216,162,128,68,72,1,241,13,158,197,20,150, +25,18,0,125,162,58,217,232,235,117,100,162,136,25,18,0,125,162,58,217,232, +235,116,36,162,145,2,226,64,15,136,108,244,117,186,178,81,73,129,113,32,7, +196,54,122,58,221,9,40,165,64,200,144,3,237,17,214,207,79,75,171,37,20,80, +200,144,3,237,17,214,207,79,75,161,37,20,138,23,18,0,124,67,103,167,165, +213,146,138,77,11,137,0,62,33,179,211,210,232,73,69,42,133,196,128,31,10, +183,2,125,89,40,163,5,196,128,31,10,183,2,125,9,40,164,96,200,144,3,224, +221,64,172,157,89,40,163,134,68,128,31,6,234,5,100,232,73,69,35,133,68,128, +31,104,142,182,125,89,40,180,0,168,144,3,237,17,214,207,161,37,22,144,19, +18,0,124,67,103,213,146,139,80,9,137,0,62,33,179,232,73,69,172,5,90,40,153, +59,68,117,179,216,166,192,77,162,137,147,136,108,246,41,180,176,219,69,19, +39,104,142,182,122,58,221,89,41,178,6,218,40,153,59,68,117,179,209,214,232, +73,77,162,6,90,40,153,56,134,207,71,91,171,37,54,152,25,104,162,100,226,27, +61,29,110,132,148,218,160,109,162,137,147,180,71,91,61,61,46,172,148,217, +67,109,20,76,157,162,58,217,233,233,116,36,166,209,67,45,20,76,156,67,103, +167,165,213,146,155,77,12,180,81,50,113,13,158,158,151,66,74,109,84,50,209, +68,201,194,173,192,159,86,74,108,193,150,138,38,78,21,110,4,250,18,83,104, +193,182,138,38,78,13,212,10,201,213,146,155,56,109,162,137,147,131,117,2, +178,116,36,166,209,194,237,20,76,157,162,58,217,245,100,167,16,2,237,20,76, +157,162,58,217,244,36,167,18,2,173,20,76,156,67,103,213,146,156,80,10,180, +81,50,113,13,159,66,74,113,97,175,221,48,216,110,64,4,42,22,189,179,0,196, +133,0,185,80,32,28,78,99,193,18,80,36,4,19,159,141,172,0,178,90,4,74,73,0, +22,209,68,201,187,129,4,2,8,3,132,64,60,36,0,171,240,84,6,149,113,72,176, +157,91,116,116,32,88,181,129,32,11,42,218,221,131,234,219,165,1,8,187,152, +255,188,231,235,248,47,86,227,105,18,2,56,175,185,255,244,17,91,40,110,173, +198,209,208,36,7,188,189,179,240,238,82,97,80,93,122,32,125,144,132,160,12, +22,162,42,7,236,161,25,232,237,105,64,158,160,230,63,205,59,127,102,11,217, +66,51,210,129,61,65,236,127,154,118,254,205,171,197,34,168,48,6,90,194,1,0, +39,75,88,72,8,9,33,186,194,80,64,76,13,214,19,2,130,96,110,150,189,0,65,6, +51,214,20,128,65,17,11,214,19,130,137,121,211,210,211,144,6,39,75,88,80,0, +201,119,235,10,8,41,86,231,71,88,80,129,79,135,186,122,133,224,34,25,69, +234,80,3,91,141,172,40,96,139,113,180,181,133,36,21,110,54,142,134,176,165, +1,176,23,213,47,0,216,134,234,215,128,111,117,181,232,128,209,3,70,230,107, +64,5,139,168,209,235,10,32,36,144,102,235,136,3,146,27,172,40,160,146,132, +103,172,40,192,115,3,117,133,28,22,113,163,69,172,41,103,1,66,188,17,145, +52,168,4,202,113,67,76,130,227,76,194,13,240,108,0,0,83,224,0,2,193,0,104, +146,84,97,48,0,1,94,192,56,169,24,145,179,192,0,5,112,8,56,16,32,128,56,18, +52,125,230,86,147,190,140,28,50,21,13,39,31,23,60,145,158,57,12,141,47,129, +6,155,194,188,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15,155, +163,201,68,14,49,39,199,197,211,116,240,242,113,197,232,18,180,254,36,3,17, +46,18,243,35,100,128,172,156,178,70,163,154,76,34,248,146,164,108,248,75, +204,141,146,28,217,115,137,27,95,27,241,173,236,162,160,224,200,2,206,9, +113,13,148,192,209,18,22,164,146,37,193,57,162,4,249,39,196,128,24,2,178, +66,213,136,68,201,16,77,209,131,31,192,242,88,96,92,191,151,34,100,136,38, +232,255,252,92,221,199,197,12,68,209,82,66,212,11,155,185,41,197,13,55,38, +3,66,213,47,135,254,72,12,162,99,133,116,112,0,1,72,66,14,16,16,50,37,202, +160,150,154,66,14,20,8,57,192,28,24,80,113,50,113,100,105,166,120,248,0,0, +179,1,65,196,201,199,20,178,36,227,224,0,2,208,54,113,240,0,1,100,11,181, +192,0,5,178,1,18,160,65,24,131,20,145,25,188,48,132,122,28,76,146,218,121, +35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,25,188,56,132,122,28,76,146, +218,121,35,180,69,145,132,108,224,0,0,120,31,128,0,0,0,40,160,45,110,23,30, +176,33,184,0,0,183,32,29,235,2,27,199,23,0,0,23,4,51,120,129,8,244,56,153, +37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0,0,0,51,120,145,8,244,56, +153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,161,8,244, +56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0,0,0,51,120,177,8, +244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120,193, +8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51,120, +209,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64,0,0,0,0,51, +120,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,32,64,0,0,0,0, +32,227,194,0,97,57,162,4,246,104,5,34,92,35,68,225,161,166,220,16,16,137, +112,52,41,73,29,185,1,65,196,201,197,145,166,153,246,72,3,137,204,120,34, +74,8,199,1,67,17,162,112,201,84,128,97,144,78,25,42,16,131,169,1,205,66,8, +35,68,225,161,166,239,128,0,10,192,64,196,104,156,50,96,0,2,172,73,240,117, +96,57,170,97,4,104,156,52,52,221,240,0,1,82,1,74,9,129,125,240,0,1,82,32, +148,25,174,137,58,23,51,190,0,0,42,69,64,195,32,156,50,96,0,2,160,81,238,2, +3,107,173,218,3,192, }; #else #error invalid endianness defines #endif #endif /* DUK_USE_ROM_OBJECTS */ -#line 1 "duk_error_macros.c" + +/* automatic undefs */ +#undef DUK__REFCINIT /* - * Error, fatal, and panic handling. + * Error and fatal handling. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ #define DUK__ERRFMT_BUFSIZE 256 /* size for formatting buffers */ #if defined(DUK_USE_VERBOSE_ERRORS) -DUK_INTERNAL void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...) { va_list ap; char msg[DUK__ERRFMT_BUFSIZE]; va_start(ap, fmt); @@ -9796,13 +11765,13 @@ DUK_INTERNAL void duk_err_handle_error_fmt(duk_hthread *thr, const char *filenam va_end(ap); /* dead code, but ensures portability (see Linux man page notes) */ } -DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) { duk_err_create_and_throw(thr, (duk_errcode_t) (line_and_code >> 24), msg, filename, (duk_int_t) (line_and_code & 0x00ffffffL)); } #else /* DUK_USE_VERBOSE_ERRORS */ -DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { duk_err_create_and_throw(thr, code); } @@ -9814,69 +11783,72 @@ DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { #if defined(DUK_USE_VERBOSE_ERRORS) #if defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index, const char *expect_name) { +DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) { DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)", - expect_name, duk_get_type_name((duk_context *) thr, index), (long) index); + expect_name, duk_get_type_name(thr, idx), (long) idx); } #else -DUK_INTERNAL void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index, const char *expect_name) { +DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) { DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)", - expect_name, duk_push_string_readable((duk_context *) thr, index), (long) index); + expect_name, duk_push_string_readable(thr, idx), (long) idx); } #endif -DUK_INTERNAL void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { +DUK_INTERNAL DUK_COLD void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_INTERNAL_ERROR); +} +DUK_INTERNAL DUK_COLD void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_ALLOC_FAILED); +} +DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, message); +} +DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, message); } -DUK_INTERNAL void duk_err_api_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t index) { - DUK_ERROR_RAW_FMT1(thr, filename, linenumber, DUK_ERR_API_ERROR, "invalid stack index %ld", (long) (index)); +DUK_INTERNAL DUK_COLD void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx) { + DUK_ERROR_RAW_FMT1(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, "invalid stack index %ld", (long) (idx)); } -DUK_INTERNAL void duk_err_api(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_API_ERROR, message); +DUK_INTERNAL DUK_COLD void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK); } -DUK_INTERNAL void duk_err_unimplemented_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_UNIMPLEMENTED_ERROR, DUK_STR_UNIMPLEMENTED); +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_ARGS); } -#if !defined(DUK_USE_BYTECODE_DUMP_SUPPORT) -DUK_INTERNAL void duk_err_unsupported_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_UNSUPPORTED_ERROR, DUK_STR_UNSUPPORTED); +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_STATE); } -#endif -DUK_INTERNAL void duk_err_internal_defmsg(duk_hthread *thr, const char *filename, duk_int_t linenumber) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_INTERNAL_ERROR, DUK_STR_INTERNAL_ERROR); -} -DUK_INTERNAL void duk_err_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_INTERNAL_ERROR, message); -} -DUK_INTERNAL void duk_err_alloc(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { - DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ALLOC_ERROR, message); +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber) { + DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_TRAP_RESULT); } #else /* The file/line arguments are NULL and 0, they're ignored by DUK_ERROR_RAW() * when non-verbose errors are used. */ -DUK_INTERNAL void duk_err_type(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_TYPE_ERROR, NULL); + +DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_errcode_t code)); +DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_errcode_t code) { + DUK_ERROR_RAW(thr, NULL, 0, code, NULL); } -DUK_INTERNAL void duk_err_api(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_API_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_ERROR); } -DUK_INTERNAL void duk_err_range(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_RANGE_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_RANGE_ERROR); } -DUK_INTERNAL void duk_err_syntax(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_SYNTAX_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_eval(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_EVAL_ERROR); } -DUK_INTERNAL void duk_err_unimplemented(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_UNIMPLEMENTED_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_reference(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_REFERENCE_ERROR); } -DUK_INTERNAL void duk_err_unsupported(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_UNSUPPORTED_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_syntax(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_SYNTAX_ERROR); } -DUK_INTERNAL void duk_err_internal(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, 0, DUK_ERR_INTERNAL_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_type(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_TYPE_ERROR); } -DUK_INTERNAL void duk_err_alloc(duk_hthread *thr) { - DUK_ERROR_RAW(thr, NULL, thr, DUK_ERR_ALLOC_ERROR, NULL); +DUK_INTERNAL DUK_COLD void duk_err_uri(duk_hthread *thr) { + duk__err_shared(thr, DUK_ERR_URI_ERROR); } #endif @@ -9884,68 +11856,56 @@ DUK_INTERNAL void duk_err_alloc(duk_hthread *thr) { * Default fatal error handler */ -DUK_INTERNAL void duk_default_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) { - DUK_UNREF(ctx); -#if defined(DUK_USE_FILE_IO) - DUK_FPRINTF(DUK_STDERR, "FATAL %ld: %s\n", (long) code, (const char *) (msg ? msg : "null")); - DUK_FFLUSH(DUK_STDERR); -#else - /* omit print */ -#endif - DUK_D(DUK_DPRINT("default fatal handler called, code %ld -> calling DUK_PANIC()", (long) code)); - DUK_PANIC(code, msg); - DUK_UNREACHABLE(); -} - -/* - * Default panic handler - */ - -#if !defined(DUK_USE_PANIC_HANDLER) -DUK_INTERNAL void duk_default_panic_handler(duk_errcode_t code, const char *msg) { -#if defined(DUK_USE_FILE_IO) - DUK_FPRINTF(DUK_STDERR, "PANIC %ld: %s (" -#if defined(DUK_USE_PANIC_ABORT) - "calling abort" -#elif defined(DUK_USE_PANIC_EXIT) - "calling exit" -#elif defined(DUK_USE_PANIC_SEGFAULT) - "segfaulting on purpose" -#else -#error no DUK_USE_PANIC_xxx macro defined -#endif - ")\n", (long) code, (const char *) (msg ? msg : "null")); - DUK_FFLUSH(DUK_STDERR); -#else - /* omit print */ - DUK_UNREF(code); +DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *msg) { + DUK_UNREF(udata); DUK_UNREF(msg); -#endif -#if defined(DUK_USE_PANIC_ABORT) - DUK_ABORT(); -#elif defined(DUK_USE_PANIC_EXIT) - DUK_EXIT(-1); -#elif defined(DUK_USE_PANIC_SEGFAULT) - /* exit() afterwards to satisfy "noreturn" */ - DUK_CAUSE_SEGFAULT(); /* SCANBUILD: "Dereference of null pointer", normal */ - DUK_EXIT(-1); + msg = msg ? msg : "NULL"; + +#if defined(DUK_USE_FATAL_HANDLER) + /* duk_config.h provided a custom default fatal handler. */ + DUK_D(DUK_DPRINT("custom default fatal error handler called: %s", msg)); + DUK_USE_FATAL_HANDLER(udata, msg); +#elif defined(DUK_USE_CPP_EXCEPTIONS) + /* With C++ use a duk_fatal_exception which user code can catch in + * a natural way. + */ + DUK_D(DUK_DPRINT("built-in default C++ fatal error handler called: %s", msg)); + throw duk_fatal_exception(msg); #else -#error no DUK_USE_PANIC_xxx macro defined + /* Default behavior is to abort() on error. There's no printout + * which makes this awkward, so it's always recommended to use an + * explicit fatal error handler. + * + * ==================================================================== + * NOTE: If you are seeing this, you are most likely dealing with an + * uncaught error. You should provide a fatal error handler in Duktape + * heap creation, and should consider using a protected call as your + * first call into an empty Duktape context to properly handle errors. + * See: + * - http://duktape.org/guide.html#error-handling + * - http://wiki.duktape.org/HowtoFatalErrors.html + * - http://duktape.org/api.html#taglist-protected + * ==================================================================== + */ + DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg)); + DUK_ABORT(); #endif - DUK_UNREACHABLE(); + DUK_D(DUK_DPRINT("fatal error handler returned, enter forever loop")); + for (;;) { + /* Loop forever to ensure we don't return. */ + } } -#endif /* !DUK_USE_PANIC_HANDLER */ +/* automatic undefs */ #undef DUK__ERRFMT_BUFSIZE -#line 1 "duk_unicode_support.c" /* * Various Unicode help functions for character classification predicates, * case conversion, decoding, etc. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* * Fast path tables @@ -10083,7 +12043,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_encode_cesu8(duk_ucodepoint_t cp, duk_u /* * Unicode codepoints above U+FFFF are encoded as surrogate * pairs here. This ensures that all CESU-8 codepoints are - * 16-bit values as expected in Ecmascript. The surrogate + * 16-bit values as expected in ECMAScript. The surrogate * pairs always get a 3-byte encoding (each) in CESU-8. * See: http://en.wikipedia.org/wiki/Surrogate_pair * @@ -10192,8 +12152,17 @@ DUK_INTERNAL duk_small_int_t duk_unicode_decode_xutf8(duk_hthread *thr, const du while (n > 0) { DUK_ASSERT(p >= ptr_start && p < ptr_end); - res = res << 6; - res += (duk_uint32_t) ((*p++) & 0x3f); + ch = (duk_uint_fast8_t) (*p++); +#if 0 + if (ch & 0xc0 != 0x80) { + /* not a continuation byte */ + p--; + *ptr = p; + *out_cp = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; + return 1; + } +#endif + res = (res << 6) + (duk_uint32_t) (ch & 0x3f); n--; } @@ -10212,9 +12181,8 @@ DUK_INTERNAL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, if (duk_unicode_decode_xutf8(thr, ptr, ptr_start, ptr_end, &cp)) { return cp; } - DUK_ERROR_INTERNAL(thr, "utf-8 decode failed"); /* XXX: 'internal error' is a bit of a misnomer */ - DUK_UNREACHABLE(); - return 0; + DUK_ERROR_INTERNAL(thr); + DUK_WO_NORETURN(return 0;); } /* Compute (extended) utf-8 length without codepoint encoding validation, @@ -10227,7 +12195,7 @@ DUK_INTERNAL duk_ucodepoint_t duk_unicode_decode_xutf8_checked(duk_hthread *thr, * chosen from several variants, based on x64 gcc -O2 testing. See: * https://github.com/svaarala/duktape/pull/422 * - * NOTE: must match src/dukutil.py:duk_unicode_unvalidated_utf8_length(). + * NOTE: must match tools/dukutil.py:duk_unicode_unvalidated_utf8_length(). */ #if defined(DUK_USE_PREFER_SIZE) @@ -10331,6 +12299,90 @@ DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *d } #endif /* DUK_USE_PREFER_SIZE */ +/* Check whether a string is UTF-8 compatible or not. */ +DUK_INTERNAL duk_bool_t duk_unicode_is_utf8_compatible(const duk_uint8_t *buf, duk_size_t len) { + duk_size_t i = 0; +#if !defined(DUK_USE_PREFER_SIZE) + duk_size_t len_safe; +#endif + + /* Many practical strings are ASCII only, so use a fast path check + * to check chunks of bytes at once with minimal branch cost. + */ +#if !defined(DUK_USE_PREFER_SIZE) + len_safe = len & ~0x03UL; + for (; i < len_safe; i += 4) { + duk_uint8_t t = buf[i] | buf[i + 1] | buf[i + 2] | buf[i + 3]; + if (DUK_UNLIKELY((t & 0x80U) != 0U)) { + /* At least one byte was outside 0x00-0x7f, break + * out to slow path (and remain there). + * + * XXX: We could also deal with the problem character + * and resume fast path later. + */ + break; + } + } +#endif + + for (; i < len;) { + duk_uint8_t t; + duk_size_t left; + duk_size_t ncont; + duk_uint32_t cp; + duk_uint32_t mincp; + + t = buf[i++]; + if (DUK_LIKELY((t & 0x80U) == 0U)) { + /* Fast path, ASCII. */ + continue; + } + + /* Non-ASCII start byte, slow path. + * + * 10xx xxxx -> continuation byte + * 110x xxxx + 1*CONT -> [0x80, 0x7ff] + * 1110 xxxx + 2*CONT -> [0x800, 0xffff], must reject [0xd800,0xdfff] + * 1111 0xxx + 3*CONT -> [0x10000, 0x10ffff] + */ + left = len - i; + if (t <= 0xdfU) { /* 1101 1111 = 0xdf */ + if (t <= 0xbfU) { /* 1011 1111 = 0xbf */ + return 0; + } + ncont = 1; + mincp = 0x80UL; + cp = t & 0x1fU; + } else if (t <= 0xefU) { /* 1110 1111 = 0xef */ + ncont = 2; + mincp = 0x800UL; + cp = t & 0x0fU; + } else if (t <= 0xf7U) { /* 1111 0111 = 0xf7 */ + ncont = 3; + mincp = 0x10000UL; + cp = t & 0x07U; + } else { + return 0; + } + if (left < ncont) { + return 0; + } + while (ncont > 0U) { + t = buf[i++]; + if ((t & 0xc0U) != 0x80U) { /* 10xx xxxx */ + return 0; + } + cp = (cp << 6) + (t & 0x3fU); + ncont--; + } + if (cp < mincp || cp > 0x10ffffUL || (cp >= 0xd800UL && cp <= 0xdfffUL)) { + return 0; + } + } + + return 1; +} + /* * Unicode range matcher * @@ -10338,7 +12390,7 @@ DUK_INTERNAL duk_size_t duk_unicode_unvalidated_utf8_length(const duk_uint8_t *d * Used for slow path Unicode matching. */ -/* Must match src/extract_chars.py, generate_match_table3(). */ +/* Must match tools/extract_chars.py, generate_match_table3(). */ DUK_LOCAL duk_uint32_t duk__uni_decode_value(duk_bitdecoder_ctx *bd_ctx) { duk_uint32_t t; @@ -10363,7 +12415,7 @@ DUK_LOCAL duk_small_int_t duk__uni_range_match(const duk_uint8_t *unitab, duk_si duk_bitdecoder_ctx bd_ctx; duk_codepoint_t prev_re; - DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); + duk_memzero(&bd_ctx, sizeof(bd_ctx)); bd_ctx.data = (const duk_uint8_t *) unitab; bd_ctx.length = (duk_size_t) unilen; @@ -10409,7 +12461,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_whitespace(duk_codepoint_t cp) { * FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; * * It also specifies any Unicode category 'Zs' characters as white - * space. These can be extracted with the "src/extract_chars.py" script. + * space. These can be extracted with the "tools/extract_chars.py" script. * Current result: * * RAW OUTPUT: @@ -10516,7 +12568,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp) * * The "UnicodeLetter" alternative of the production allows letters * from various Unicode categories. These can be extracted with the - * "src/extract_chars.py" script. + * "tools/extract_chars.py" script. * * Because the result has hundreds of Unicode codepoint ranges, matching * for any values >= 0x80 are done using a very slow range-by-range scan @@ -10547,7 +12599,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_start(duk_codepoint_t cp) /* Non-ASCII slow path (range-by-range linear comparison), very slow */ -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) if (duk__uni_range_match(duk_unicode_ids_noa, (duk_size_t) sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp)) { @@ -10613,7 +12665,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp) * The matching code reuses the "identifier start" tables, and then * consults a separate range set for characters in "identifier part" * but not in "identifier start". These can be extracted with the - * "src/extract_chars.py" script. + * "tools/extract_chars.py" script. * * UnicodeCombiningMark -> categories Mn, Mc * UnicodeDigit -> categories Nd @@ -10637,7 +12689,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_identifier_part(duk_codepoint_t cp) /* Non-ASCII slow path (range-by-range linear comparison), very slow */ -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) if (duk__uni_range_match(duk_unicode_ids_noa, sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp) || @@ -10696,7 +12748,7 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp) { /* Non-ASCII slow path (range-by-range linear comparison), very slow */ -#ifdef DUK_USE_SOURCE_NONBMP +#if defined(DUK_USE_SOURCE_NONBMP) if (duk__uni_range_match(duk_unicode_ids_noa, sizeof(duk_unicode_ids_noa), (duk_codepoint_t) cp) && @@ -10728,14 +12780,14 @@ DUK_INTERNAL duk_small_int_t duk_unicode_is_letter(duk_codepoint_t cp) { /* * Complex case conversion helper which decodes a bit-packed conversion - * control stream generated by unicode/extract_caseconv.py. The conversion + * control stream generated by tools/extract_caseconv.py. The conversion * is very slow because it runs through the conversion data in a linear * fashion to save space (which is why ASCII characters have a special * fast path before arriving here). * * The particular bit counts etc have been determined experimentally to * be small but still sufficient, and must match the Python script - * (src/extract_caseconv.py). + * (tools/extract_caseconv.py). * * The return value is the case converted codepoint or -1 if the conversion * results in multiple characters (this is useful for regexp Canonicalization @@ -10759,8 +12811,8 @@ duk_codepoint_t duk__slow_case_conversion(duk_hthread *thr, duk_codepoint_t start_i; duk_codepoint_t start_o; - DUK_UNREF(thr); DUK_ASSERT(bd_ctx != NULL); + DUK_UNREF(thr); DUK_DDD(DUK_DDDPRINT("slow case conversion for codepoint: %ld", (long) cp)); @@ -10795,7 +12847,7 @@ duk_codepoint_t duk__slow_case_conversion(duk_hthread *thr, } /* 1:1 conversion */ - n = (duk_small_int_t) duk_bd_decode(bd_ctx, 6); + n = (duk_small_int_t) duk_bd_decode(bd_ctx, 7); DUK_DDD(DUK_DDDPRINT("checking 1:1 conversions (count %ld)", (long) n)); while (n--) { start_i = (duk_codepoint_t) duk_bd_decode(bd_ctx, 16); @@ -10915,7 +12967,7 @@ duk_codepoint_t duk__case_transform_helper(duk_hthread *thr, } /* 1:1 or special conversions, but not locale/context specific: script generated rules */ - DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx)); + duk_memzero(&bd_ctx, sizeof(bd_ctx)); if (uppercase) { bd_ctx.data = (const duk_uint8_t *) duk_unicode_caseconv_uc; bd_ctx.length = (duk_size_t) sizeof(duk_unicode_caseconv_uc); @@ -10942,15 +12994,14 @@ duk_codepoint_t duk__case_transform_helper(duk_hthread *thr, * Replace valstack top with case converted version. */ -DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_small_int_t uppercase) { - duk_context *ctx = (duk_context *) thr; +DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase) { duk_hstring *h_input; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; const duk_uint8_t *p, *p_start, *p_end; duk_codepoint_t prev, curr, next; - h_input = duk_require_hstring(ctx, -1); + h_input = duk_require_hstring(thr, -1); /* Accept symbols. */ DUK_ASSERT(h_input != NULL); bw = &bw_alloc; @@ -10970,7 +13021,7 @@ DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_small_in curr = next; next = -1; if (p < p_end) { - next = (int) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); + next = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); } else { /* end of input and last char has been processed */ if (curr < 0) { @@ -10997,11 +13048,12 @@ DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_small_in } DUK_BW_COMPACT(thr, bw); - duk_to_string(ctx, -1); /* invalidates h_buf pointer */ - duk_remove(ctx, -2); + (void) duk_buffer_to_string(thr, -1); /* Safe, output is encoded. */ + /* invalidates h_buf pointer */ + duk_remove_m2(thr); } -#ifdef DUK_USE_REGEXP_SUPPORT +#if defined(DUK_USE_REGEXP_SUPPORT) /* * Canonicalize() abstract operation needed for canonicalization of individual @@ -11113,12 +13165,159 @@ DUK_INTERNAL const duk_uint16_t duk_unicode_re_ranges_not_wordchar[10] = { }; #endif /* DUK_USE_REGEXP_SUPPORT */ -#line 1 "duk_util_misc.c" /* - * Misc util stuff + * Macro support functions for reading/writing raw data. + * + * These are done using memcpy to ensure they're valid even for unaligned + * reads/writes on platforms where alignment counts. On x86 at least gcc + * is able to compile these into a bswap+mov. "Always inline" is used to + * ensure these macros compile to minimal code. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +union duk__u16_union { + duk_uint8_t b[2]; + duk_uint16_t x; +}; +typedef union duk__u16_union duk__u16_union; + +union duk__u32_union { + duk_uint8_t b[4]; + duk_uint32_t x; +}; +typedef union duk__u32_union duk__u32_union; + +#if defined(DUK_USE_64BIT_OPS) +union duk__u64_union { + duk_uint8_t b[8]; + duk_uint64_t x; +}; +typedef union duk__u64_union duk__u64_union; +#endif + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_read_u16_be(const duk_uint8_t *p) { + duk__u16_union u; + duk_memcpy((void *) u.b, (const void *) p, (size_t) 2); + u.x = DUK_NTOH16(u.x); + return u.x; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_read_u32_be(const duk_uint8_t *p) { + duk__u32_union u; + duk_memcpy((void *) u.b, (const void *) p, (size_t) 4); + u.x = DUK_NTOH32(u.x); + return u.x; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_read_float_be(const duk_uint8_t *p) { + duk_float_union fu; + duk_memcpy((void *) fu.uc, (const void *) p, (size_t) 4); + duk_fltunion_big_to_host(&fu); + return fu.f; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_read_double_be(const duk_uint8_t *p) { + duk_double_union du; + duk_memcpy((void *) du.uc, (const void *) p, (size_t) 8); + duk_dblunion_big_to_host(&du); + return du.d; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint16_t duk_raw_readinc_u16_be(const duk_uint8_t **p) { + duk_uint16_t res = duk_raw_read_u16_be(*p); + *p += 2; + return res; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_uint32_t duk_raw_readinc_u32_be(const duk_uint8_t **p) { + duk_uint32_t res = duk_raw_read_u32_be(*p); + *p += 4; + return res; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_float_t duk_raw_readinc_float_be(const duk_uint8_t **p) { + duk_float_t res = duk_raw_read_float_be(*p); + *p += 4; + return res; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_raw_readinc_double_be(const duk_uint8_t **p) { + duk_double_t res = duk_raw_read_double_be(*p); + *p += 8; + return res; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u16_be(duk_uint8_t *p, duk_uint16_t val) { + duk__u16_union u; + u.x = DUK_HTON16(val); + duk_memcpy((void *) p, (const void *) u.b, (size_t) 2); +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_u32_be(duk_uint8_t *p, duk_uint32_t val) { + duk__u32_union u; + u.x = DUK_HTON32(val); + duk_memcpy((void *) p, (const void *) u.b, (size_t) 4); +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_float_be(duk_uint8_t *p, duk_float_t val) { + duk_float_union fu; + fu.f = val; + duk_fltunion_host_to_big(&fu); + duk_memcpy((void *) p, (const void *) fu.uc, (size_t) 4); +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_write_double_be(duk_uint8_t *p, duk_double_t val) { + duk_double_union du; + du.d = val; + duk_dblunion_host_to_big(&du); + duk_memcpy((void *) p, (const void *) du.uc, (size_t) 8); +} + +DUK_INTERNAL duk_small_int_t duk_raw_write_xutf8(duk_uint8_t *p, duk_ucodepoint_t val) { + duk_small_int_t len = duk_unicode_encode_xutf8(val, p); + return len; +} + +DUK_INTERNAL duk_small_int_t duk_raw_write_cesu8(duk_uint8_t *p, duk_ucodepoint_t val) { + duk_small_int_t len = duk_unicode_encode_cesu8(val, p); + return len; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u16_be(duk_uint8_t **p, duk_uint16_t val) { + duk_raw_write_u16_be(*p, val); + *p += 2; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_u32_be(duk_uint8_t **p, duk_uint32_t val) { + duk_raw_write_u32_be(*p, val); + *p += 4; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_float_be(duk_uint8_t **p, duk_float_t val) { + duk_raw_write_float_be(*p, val); + *p += 4; +} + +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_raw_writeinc_double_be(duk_uint8_t **p, duk_double_t val) { + duk_raw_write_double_be(*p, val); + *p += 8; +} + +DUK_INTERNAL void duk_raw_writeinc_xutf8(duk_uint8_t **p, duk_ucodepoint_t val) { + duk_small_int_t len = duk_unicode_encode_xutf8(val, *p); + *p += len; +} + +DUK_INTERNAL void duk_raw_writeinc_cesu8(duk_uint8_t **p, duk_ucodepoint_t val) { + duk_small_int_t len = duk_unicode_encode_cesu8(val, *p); + *p += len; +} +/* + * Misc util stuff. + */ + +/* #include duk_internal.h -> already included */ /* * Lowercase digits for radix values 2 to 36. Also doubles as lowercase @@ -11277,45 +13476,6 @@ DUK_INTERNAL const duk_uint16_t duk_hex_enctab[256] = { }; #endif /* DUK_USE_HEX_FASTPATH */ -/* - * Table for base-64 encoding - */ - -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_INTERNAL const duk_uint8_t duk_base64_enctab[64] = { - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* A...P */ - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, /* Q...f */ - 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, /* g...v */ - 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f /* w.../ */ -}; -#endif /* DUK_USE_BASE64_FASTPATH */ - -/* - * Table for base-64 decoding - */ - -#if defined(DUK_USE_BASE64_FASTPATH) -DUK_INTERNAL const duk_int8_t duk_base64_dectab[256] = { - /* -1 = error, -2 = allowed whitespace, -3 = padding ('='), 0...63 decoded bytes */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, /* 0x00...0x0f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10...0x1f */ - -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20...0x2f */ - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1, /* 0x30...0x3f */ - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40...0x4f */ - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50...0x5f */ - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60...0x6f */ - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70...0x7f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80...0x8f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90...0x9f */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xa0...0xaf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xb0...0xbf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xc0...0xcf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xd0...0xdf */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xe0...0xef */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xf0...0xff */ -}; -#endif /* DUK_USE_BASE64_FASTPATH */ - /* * Arbitrary byteswap for potentially unaligned values * @@ -11336,101 +13496,22 @@ DUK_INTERNAL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len) { } } #endif -#line 1 "duk_util_hashprime.c" /* - * Round a number upwards to a prime (not usually the nearest one). - * - * Uses a table of successive 32-bit primes whose ratio is roughly - * constant. This keeps the relative upwards 'rounding error' bounded - * and the data size small. A simple 'predict-correct' compression is - * used to compress primes to one byte per prime. See genhashsizes.py - * for details. - * - * The minimum prime returned here must be coordinated with the possible - * probe sequence steps in duk_hobject and duk_heap stringtable. + * Hobject ECMAScript [[Class]]. */ -/* include removed: duk_internal.h */ - -/* Awkward inclusion condition: drop out of compilation if not needed by any - * call site: object hash part or probing stringtable. - */ -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) - -/* hash size ratio goal, must match genhashsizes.py */ -#define DUK__HASH_SIZE_RATIO 1177 /* floor(1.15 * (1 << 10)) */ - -/* prediction corrections for prime list (see genhashsizes.py) */ -DUK_LOCAL const duk_int8_t duk__hash_size_corrections[] = { - 17, /* minimum prime */ - 4, 3, 4, 1, 4, 1, 1, 2, 2, 2, 2, 1, 6, 6, 9, 5, 1, 2, 2, 5, 1, 3, 3, 3, - 5, 4, 4, 2, 4, 8, 3, 4, 23, 2, 4, 7, 8, 11, 2, 12, 15, 10, 1, 1, 5, 1, 5, - 8, 9, 17, 14, 10, 7, 5, 2, 46, 21, 1, 9, 9, 4, 4, 10, 23, 36, 6, 20, 29, - 18, 6, 19, 21, 16, 11, 5, 5, 48, 9, 1, 39, 14, 8, 4, 29, 9, 1, 15, 48, 12, - 22, 6, 15, 27, 4, 2, 17, 28, 8, 9, 4, 5, 8, 3, 3, 8, 37, 11, 15, 8, 30, - 43, 6, 33, 41, 5, 20, 32, 41, 38, 24, 77, 14, 19, 11, 4, 35, 18, 19, 41, - 10, 23, 16, 9, 2, - -1 -}; - -/* probe steps (see genhashsizes.py), currently assumed to be 32 entries long - * (DUK_UTIL_GET_HASH_PROBE_STEP macro). - */ -DUK_INTERNAL duk_uint8_t duk_util_probe_steps[32] = { - 2, 3, 5, 7, 11, 13, 19, 31, 41, 47, 59, 67, 73, 79, 89, 101, 103, 107, - 109, 127, 137, 139, 149, 157, 163, 167, 173, 181, 191, 193, 197, 199 -}; - -DUK_INTERNAL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size) { - const duk_int8_t *p = duk__hash_size_corrections; - duk_uint32_t curr; - - curr = (duk_uint32_t) *p++; - for (;;) { - duk_small_int_t t = (duk_small_int_t) *p++; - if (t < 0) { - /* may happen if size is very close to 2^32-1 */ - break; - } - - /* prediction: portable variant using doubles if 64-bit values not available */ -#ifdef DUK_USE_64BIT_OPS - curr = (duk_uint32_t) ((((duk_uint64_t) curr) * ((duk_uint64_t) DUK__HASH_SIZE_RATIO)) >> 10); -#else - /* 32-bit x 11-bit = 43-bit, fits accurately into a double */ - curr = (duk_uint32_t) DUK_FLOOR(((double) curr) * ((double) DUK__HASH_SIZE_RATIO) / 1024.0); -#endif - - /* correction */ - curr += t; - - DUK_DDD(DUK_DDDPRINT("size=%ld, curr=%ld", (long) size, (long) curr)); - - if (curr >= size) { - return curr; - } - } - return 0; -} - -#endif /* DUK_USE_HOBJECT_HASH_PART || DUK_USE_STRTAB_PROBE */ -#line 1 "duk_hobject_class.c" -/* - * Hobject Ecmascript [[Class]]. - */ - -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ #if (DUK_STRIDX_UC_ARGUMENTS > 255) #error constant too large #endif -#if (DUK_STRIDX_ARRAY > 255) +#if (DUK_STRIDX_UC_ARRAY > 255) #error constant too large #endif #if (DUK_STRIDX_UC_BOOLEAN > 255) #error constant too large #endif -#if (DUK_STRIDX_DATE > 255) +#if (DUK_STRIDX_UC_DATE > 255) #error constant too large #endif #if (DUK_STRIDX_UC_ERROR > 255) @@ -11466,9 +13547,6 @@ DUK_INTERNAL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size) { #if (DUK_STRIDX_DEC_ENV > 255) #error constant too large #endif -#if (DUK_STRIDX_UC_BUFFER > 255) -#error constant too large -#endif #if (DUK_STRIDX_UC_POINTER > 255) #error constant too large #endif @@ -11514,23 +13592,23 @@ DUK_INTERNAL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size) { /* Note: assumes that these string indexes are 8-bit, genstrings.py must ensure that */ DUK_INTERNAL duk_uint8_t duk_class_number_to_stridx[32] = { - DUK_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ - DUK_STRIDX_UC_ARGUMENTS, - DUK_STRIDX_ARRAY, - DUK_STRIDX_UC_BOOLEAN, - DUK_STRIDX_DATE, - DUK_STRIDX_UC_ERROR, + DUK_STRIDX_EMPTY_STRING, /* NONE, intentionally empty */ + DUK_STRIDX_UC_OBJECT, + DUK_STRIDX_UC_ARRAY, DUK_STRIDX_UC_FUNCTION, + DUK_STRIDX_UC_ARGUMENTS, + DUK_STRIDX_UC_BOOLEAN, + DUK_STRIDX_UC_DATE, + DUK_STRIDX_UC_ERROR, DUK_STRIDX_JSON, DUK_STRIDX_MATH, DUK_STRIDX_UC_NUMBER, - DUK_STRIDX_UC_OBJECT, DUK_STRIDX_REG_EXP, DUK_STRIDX_UC_STRING, DUK_STRIDX_GLOBAL, + DUK_STRIDX_UC_SYMBOL, DUK_STRIDX_OBJ_ENV, DUK_STRIDX_DEC_ENV, - DUK_STRIDX_UC_BUFFER, DUK_STRIDX_UC_POINTER, DUK_STRIDX_UC_THREAD, DUK_STRIDX_ARRAY_BUFFER, @@ -11547,7 +13625,6 @@ DUK_INTERNAL duk_uint8_t duk_class_number_to_stridx[32] = { DUK_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ DUK_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ }; -#line 1 "duk_alloc_default.c" /* * Default allocation functions. * @@ -11555,7 +13632,7 @@ DUK_INTERNAL duk_uint8_t duk_class_number_to_stridx[32] = { * a NULL or a unique pointer which is a no-op for free. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ #if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) DUK_INTERNAL void *duk_default_alloc_function(void *udata, duk_size_t size) { @@ -11582,45 +13659,44 @@ DUK_INTERNAL void duk_default_free_function(void *udata, void *ptr) { DUK_ANSI_FREE(ptr); } #endif /* DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS */ -#line 1 "duk_api_buffer.c" /* * Buffer */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL void *duk_resize_buffer(duk_context *ctx, duk_idx_t index, duk_size_t new_size) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void *duk_resize_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t new_size) { duk_hbuffer_dynamic *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index); + h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx); DUK_ASSERT(h != NULL); if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) { DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); + DUK_WO_NORETURN(return NULL;); } - /* maximum size check is handled by callee */ + /* Maximum size check is handled by callee. */ duk_hbuffer_resize(thr, h, new_size); return DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h); } -DUK_EXTERNAL void *duk_steal_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void *duk_steal_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { duk_hbuffer_dynamic *h; void *ptr; duk_size_t sz; - DUK_ASSERT(ctx != NULL); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, index); + h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx); DUK_ASSERT(h != NULL); if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) { DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); + DUK_WO_NORETURN(return NULL;); } /* Forget the previous allocation, setting size to 0 and alloc to @@ -11639,24 +13715,23 @@ DUK_EXTERNAL void *duk_steal_buffer(duk_context *ctx, duk_idx_t index, duk_size_ return ptr; } -DUK_EXTERNAL void duk_config_buffer(duk_context *ctx, duk_idx_t index, void *ptr, duk_size_t len) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_config_buffer(duk_hthread *thr, duk_idx_t idx, void *ptr, duk_size_t len) { duk_hbuffer_external *h; - DUK_ASSERT(ctx != NULL); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_external *) duk_require_hbuffer(ctx, index); + h = (duk_hbuffer_external *) duk_require_hbuffer(thr, idx); DUK_ASSERT(h != NULL); if (!DUK_HBUFFER_HAS_EXTERNAL(h)) { DUK_ERROR_TYPE(thr, DUK_STR_WRONG_BUFFER_TYPE); + DUK_WO_NORETURN(return;); } DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h)); DUK_HBUFFER_EXTERNAL_SET_DATA_PTR(thr->heap, h, ptr); DUK_HBUFFER_EXTERNAL_SET_SIZE(h, len); } -#line 1 "duk_api_bytecode.c" /* * Bytecode dump/load * @@ -11669,37 +13744,37 @@ DUK_EXTERNAL void duk_config_buffer(duk_context *ctx, duk_idx_t index, void *ptr * validated which is not easy to do with indirect register references etc. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ #if defined(DUK_USE_BYTECODE_DUMP_SUPPORT) -#define DUK__SER_MARKER 0xff -#define DUK__SER_VERSION 0x00 +#define DUK__SER_MARKER 0xbf #define DUK__SER_STRING 0x00 #define DUK__SER_NUMBER 0x01 #define DUK__BYTECODE_INITIAL_ALLOC 256 +#define DUK__NO_FORMALS 0xffffffffUL /* * Dump/load helpers, xxx_raw() helpers do no buffer checks */ -DUK_LOCAL duk_uint8_t *duk__load_string_raw(duk_context *ctx, duk_uint8_t *p) { +DUK_LOCAL const duk_uint8_t *duk__load_string_raw(duk_hthread *thr, const duk_uint8_t *p) { duk_uint32_t len; - len = DUK_RAW_READ_U32_BE(p); - duk_push_lstring(ctx, (const char *) p, len); + len = DUK_RAW_READINC_U32_BE(p); + duk_push_lstring(thr, (const char *) p, len); p += len; return p; } -DUK_LOCAL duk_uint8_t *duk__load_buffer_raw(duk_context *ctx, duk_uint8_t *p) { +DUK_LOCAL const duk_uint8_t *duk__load_buffer_raw(duk_hthread *thr, const duk_uint8_t *p) { duk_uint32_t len; duk_uint8_t *buf; - len = DUK_RAW_READ_U32_BE(p); - buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len); + len = DUK_RAW_READINC_U32_BE(p); + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len); DUK_ASSERT(buf != NULL); - DUK_MEMCPY((void *) buf, (const void *) p, (size_t) len); + duk_memcpy((void *) buf, (const void *) p, (size_t) len); p += len; return p; } @@ -11713,8 +13788,8 @@ DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) { len = DUK_HSTRING_GET_BYTELEN(h); DUK_ASSERT(len <= 0xffffffffUL); /* string limits */ tmp32 = (duk_uint32_t) len; - DUK_RAW_WRITE_U32_BE(p, tmp32); - DUK_MEMCPY((void *) p, + DUK_RAW_WRITEINC_U32_BE(p, tmp32); + duk_memcpy((void *) p, (const void *) DUK_HSTRING_GET_DATA(h), len); p += len; @@ -11732,10 +13807,11 @@ DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, d len = DUK_HBUFFER_GET_SIZE(h); DUK_ASSERT(len <= 0xffffffffUL); /* buffer limits */ tmp32 = (duk_uint32_t) len; - DUK_RAW_WRITE_U32_BE(p, tmp32); - DUK_MEMCPY((void *) p, - (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), - len); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); + /* When len == 0, buffer data pointer may be NULL. */ + duk_memcpy_unsafe((void *) p, + (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), + len); p += len; return p; } @@ -11744,7 +13820,7 @@ DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, d duk_hstring *h_str; duk_tval *tv; - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx)); + tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); if (tv != NULL && DUK_TVAL_IS_STRING(tv)) { h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); @@ -11753,7 +13829,7 @@ DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, d DUK_ASSERT(h_str != NULL); } DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(h_str), p); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(h_str), p); p = duk__dump_hstring_raw(p, h_str); return p; } @@ -11761,17 +13837,17 @@ DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, d DUK_LOCAL duk_uint8_t *duk__dump_buffer_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) { duk_tval *tv; - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx)); + tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); if (tv != NULL && DUK_TVAL_IS_BUFFER(tv)) { duk_hbuffer *h_buf; h_buf = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h_buf != NULL); DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HBUFFER_GET_SIZE(h_buf), p); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HBUFFER_GET_SIZE(h_buf), p); p = duk__dump_hbuffer_raw(thr, p, h_buf); } else { - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); - DUK_RAW_WRITE_U32_BE(p, 0); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); + DUK_RAW_WRITEINC_U32_BE(p, 0); } return p; } @@ -11780,28 +13856,24 @@ DUK_LOCAL duk_uint8_t *duk__dump_uint32_prop(duk_hthread *thr, duk_uint8_t *p, d duk_tval *tv; duk_uint32_t val; - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx)); + tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx); if (tv != NULL && DUK_TVAL_IS_NUMBER(tv)) { val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv); } else { val = def_value; } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); - DUK_RAW_WRITE_U32_BE(p, val); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); + DUK_RAW_WRITEINC_U32_BE(p, val); return p; } DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { - duk_tval *tv; + duk_hobject *h; - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_VARMAP(thr)); - if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; + h = duk_hobject_get_varmap(thr, (duk_hobject *) func); + if (h != NULL) { duk_uint_fast32_t i; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - /* We know _Varmap only has own properties so walk property * table directly. We also know _Varmap is dense and all * values are numbers; assert for these. GC and finalizers @@ -11827,57 +13899,58 @@ DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bu #endif DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(key) + 4, p); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(key) + 4U, p); p = duk__dump_hstring_raw(p, key); - DUK_RAW_WRITE_U32_BE(p, val); + DUK_RAW_WRITEINC_U32_BE(p, val); } } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); - DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Varmap */ + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); + DUK_RAW_WRITEINC_U32_BE(p, 0); /* end of _Varmap */ return p; } DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) { - duk_tval *tv; + duk_harray *h; - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_FORMALS(thr)); - if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - duk_uint_fast32_t i; + h = duk_hobject_get_formals(thr, (duk_hobject *) func); + if (h != NULL) { + duk_uint32_t i; - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - - /* We know _Formals is dense and all entries will be in the - * array part. GC and finalizers shouldn't affect _Formals - * so side effects should be fine. + /* Here we rely on _Formals being a dense array containing + * strings. This should be the case unless _Formals has been + * tweaked by the application (which we don't support right + * now). */ - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { + + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); + DUK_ASSERT(h->length != DUK__NO_FORMALS); /* limits */ + DUK_RAW_WRITEINC_U32_BE(p, h->length); + + for (i = 0; i < h->length; i++) { duk_tval *tv_val; duk_hstring *varname; - tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i); + tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, (duk_hobject *) h, i); DUK_ASSERT(tv_val != NULL); - if (DUK_TVAL_IS_STRING(tv_val)) { - /* Array is dense and contains only strings, but ASIZE may - * be larger than used part and there are UNUSED entries. - */ - varname = DUK_TVAL_GET_STRING(tv_val); - DUK_ASSERT(varname != NULL); + DUK_ASSERT(DUK_TVAL_IS_STRING(tv_val)); - DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(varname), p); - p = duk__dump_hstring_raw(p, varname); - } + varname = DUK_TVAL_GET_STRING(tv_val); + DUK_ASSERT(varname != NULL); + DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1); + + DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(varname), p); + p = duk__dump_hstring_raw(p, varname); } + } else { + DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit marker to indicate missing _Formals")); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p); + DUK_RAW_WRITEINC_U32_BE(p, DUK__NO_FORMALS); /* marker: no formals */ } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); - DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Formals */ return p; } -static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) { - duk_hthread *thr; +static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) { duk_tval *tv, *tv_end; duk_instr_t *ins, *ins_end; duk_hobject **fn, **fn_end; @@ -11887,76 +13960,73 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func duk_uint16_t tmp16; duk_double_t d; - thr = (duk_hthread *) ctx; - DUK_UNREF(ctx); - DUK_UNREF(thr); - DUK_DD(DUK_DDPRINT("dumping function %p to %p: " "consts=[%p,%p[ (%ld bytes, %ld items), " "funcs=[%p,%p[ (%ld bytes, %ld items), " "code=[%p,%p[ (%ld bytes, %ld items)", (void *) func, (void *) p, - (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func), - (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func), - (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func), - (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func), - (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func), - (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(thr->heap, func), - (long) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func))); + (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func), + (void *) DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_CONSTS_SIZE(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func), + (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func), + (void *) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_FUNCS_SIZE(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func), + (void *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func), + (void *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_CODE_SIZE(thr->heap, func), + (long) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func))); DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL); /* ensures no overflow */ - count_instr = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func); - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p); + count_instr = (duk_uint32_t) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3U * 4U + 2U * 2U + 3U * 4U + count_instr * 4U, p); /* Fixed header info. */ tmp32 = count_instr; - DUK_RAW_WRITE_U32_BE(p, tmp32); - tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func); - DUK_RAW_WRITE_U32_BE(p, tmp32); - tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func); - DUK_RAW_WRITE_U32_BE(p, tmp32); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); + tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); + tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); tmp16 = func->nregs; - DUK_RAW_WRITE_U16_BE(p, tmp16); + DUK_RAW_WRITEINC_U16_BE(p, tmp16); tmp16 = func->nargs; - DUK_RAW_WRITE_U16_BE(p, tmp16); + DUK_RAW_WRITEINC_U16_BE(p, tmp16); #if defined(DUK_USE_DEBUGGER_SUPPORT) tmp32 = func->start_line; - DUK_RAW_WRITE_U32_BE(p, tmp32); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); tmp32 = func->end_line; - DUK_RAW_WRITE_U32_BE(p, tmp32); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); #else - DUK_RAW_WRITE_U32_BE(p, 0); - DUK_RAW_WRITE_U32_BE(p, 0); + DUK_RAW_WRITEINC_U32_BE(p, 0); + DUK_RAW_WRITEINC_U32_BE(p, 0); #endif - tmp32 = ((duk_heaphdr *) func)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK; - DUK_RAW_WRITE_U32_BE(p, tmp32); + tmp32 = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) func); /* masks flags, only duk_hobject flags */ + tmp32 &= ~(DUK_HOBJECT_FLAG_HAVE_FINALIZER); /* finalizer flag is lost */ + DUK_RAW_WRITEINC_U32_BE(p, tmp32); /* Bytecode instructions: endian conversion needed unless * platform is big endian. */ - ins = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func); - ins_end = DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func); + ins = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func); + ins_end = DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func); DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr); #if defined(DUK_USE_INTEGER_BE) - DUK_MEMCPY((void *) p, (const void *) ins, (size_t) (ins_end - ins)); + duk_memcpy_unsafe((void *) p, (const void *) ins, (size_t) (ins_end - ins)); p += (size_t) (ins_end - ins); #else while (ins != ins_end) { tmp32 = (duk_uint32_t) (*ins); - DUK_RAW_WRITE_U32_BE(p, tmp32); + DUK_RAW_WRITEINC_U32_BE(p, tmp32); ins++; } #endif /* Constants: variable size encoding. */ - tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func); - tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func); + tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func); + tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func); while (tv != tv_end) { /* constants are strings or numbers now */ DUK_ASSERT(DUK_TVAL_IS_STRING(tv) || @@ -11966,22 +14036,22 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */ - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p), + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 4U + DUK_HSTRING_GET_BYTELEN(h_str), p); *p++ = DUK__SER_STRING; p = duk__dump_hstring_raw(p, h_str); } else { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p); + p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 8U, p); *p++ = DUK__SER_NUMBER; d = DUK_TVAL_GET_NUMBER(tv); - DUK_RAW_WRITE_DOUBLE_BE(p, d); + DUK_RAW_WRITEINC_DOUBLE_BE(p, d); } tv++; } /* Inner functions recursively. */ - fn = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func); - fn_end = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func); + fn = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func); + fn_end = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func); while (fn != fn_end) { /* XXX: This causes recursion up to inner function depth * which is normally not an issue, e.g. mark-and-sweep uses @@ -11989,11 +14059,13 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func * this would mean some sort of a work list or just refusing * to serialize deep functions. */ - DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(*fn)); - p = duk__dump_func(ctx, (duk_hcompiledfunction *) *fn, bw_ctx, p); + DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(*fn)); + p = duk__dump_func(thr, (duk_hcompfunc *) *fn, bw_ctx, p); fn++; } + /* Lexenv and varenv are not dumped. */ + /* Object extra properties. * * There are some difference between function templates and functions. @@ -12002,9 +14074,15 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func */ p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs); +#if defined(DUK_USE_FUNC_NAME_PROPERTY) p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME); +#endif +#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME); +#endif +#if defined(DUK_USE_PC2LINE) p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE); +#endif p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func); p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func); @@ -12027,9 +14105,8 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \ } while (0) -static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t *p_end) { - duk_hthread *thr; - duk_hcompiledfunction *h_fun; +static const duk_uint8_t *duk__load_func(duk_hthread *thr, const duk_uint8_t *p, const duk_uint8_t *p_end) { + duk_hcompfunc *h_fun; duk_hbuffer *h_data; duk_size_t data_size; duk_uint32_t count_instr, count_const, count_funcs; @@ -12041,21 +14118,23 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t duk_idx_t idx_base; duk_tval *tv1; duk_uarridx_t arr_idx; + duk_uarridx_t arr_limit; + duk_hobject *func_env; + duk_bool_t need_pop; /* XXX: There's some overlap with duk_js_closure() here, but * seems difficult to share code. Ensure that the final function * looks the same as created by duk_js_closure(). */ - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; + DUK_ASSERT(thr != NULL); - DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (void *) p, (void *) p_end)); + DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (const void *) p, (const void *) p_end)); DUK__ASSERT_LEFT(3 * 4); - count_instr = DUK_RAW_READ_U32_BE(p); - count_const = DUK_RAW_READ_U32_BE(p); - count_funcs = DUK_RAW_READ_U32_BE(p); + count_instr = DUK_RAW_READINC_U32_BE(p); + count_const = DUK_RAW_READINC_U32_BE(p); + count_funcs = DUK_RAW_READINC_U32_BE(p); data_size = sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs + @@ -12069,48 +14148,52 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * inner functions being loaded. Require enough space to handle * large functions correctly. */ - duk_require_stack(ctx, 2 + count_const + count_funcs); - idx_base = duk_get_top(ctx); + duk_require_stack(thr, (duk_idx_t) (2 + count_const + count_funcs)); + idx_base = duk_get_top(thr); /* Push function object, init flags etc. This must match * duk_js_push_closure() quite carefully. */ - duk_push_compiledfunction(ctx); - h_fun = duk_get_hcompiledfunction(ctx, -1); + h_fun = duk_push_hcompfunc(thr); DUK_ASSERT(h_fun != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) h_fun)); - DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, h_fun) == NULL); - DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr->heap, h_fun) == NULL); - DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr->heap, h_fun) == NULL); + DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) h_fun)); + DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun) == NULL); + DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, h_fun) == NULL); + DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, h_fun) == NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - h_fun->nregs = DUK_RAW_READ_U16_BE(p); - h_fun->nargs = DUK_RAW_READ_U16_BE(p); + h_fun->nregs = DUK_RAW_READINC_U16_BE(p); + h_fun->nargs = DUK_RAW_READINC_U16_BE(p); #if defined(DUK_USE_DEBUGGER_SUPPORT) - h_fun->start_line = DUK_RAW_READ_U32_BE(p); - h_fun->end_line = DUK_RAW_READ_U32_BE(p); + h_fun->start_line = DUK_RAW_READINC_U32_BE(p); + h_fun->end_line = DUK_RAW_READINC_U32_BE(p); #else p += 8; /* skip line info */ #endif - /* duk_hcompiledfunction flags; quite version specific */ - tmp32 = DUK_RAW_READ_U32_BE(p); - DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32); + /* duk_hcompfunc flags; quite version specific */ + tmp32 = DUK_RAW_READINC_U32_BE(p); + DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32); /* masks flags to only change duk_hobject flags */ - /* standard prototype */ + /* standard prototype (no need to set here, already set) */ + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); +#if 0 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); +#endif /* assert just a few critical flags */ DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&h_fun->obj)); - DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj)); + DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj)); + DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj)); + DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj)); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&h_fun->obj)); + DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj)); /* Create function 'data' buffer but don't attach it yet. */ - fun_data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, data_size); + fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, data_size); DUK_ASSERT(fun_data != NULL); /* Load bytecode instructions. */ @@ -12118,14 +14201,14 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t)); #if defined(DUK_USE_INTEGER_BE) q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; - DUK_MEMCPY((void *) q, + duk_memcpy((void *) q, (const void *) p, sizeof(duk_instr_t) * count_instr); p += sizeof(duk_instr_t) * count_instr; #else q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs; for (n = count_instr; n > 0; n--) { - *((duk_instr_t *) (void *) q) = DUK_RAW_READ_U32_BE(p); + *((duk_instr_t *) (void *) q) = DUK_RAW_READINC_U32_BE(p); q += sizeof(duk_instr_t); } #endif @@ -12133,10 +14216,10 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t /* Load constants onto value stack but don't yet copy to buffer. */ for (n = count_const; n > 0; n--) { DUK__ASSERT_LEFT(1); - const_type = DUK_RAW_READ_U8(p); + const_type = DUK_RAW_READINC_U8(p); switch (const_type) { case DUK__SER_STRING: { - p = duk__load_string_raw(ctx, p); + p = duk__load_string_raw(thr, p); break; } case DUK__SER_NUMBER: { @@ -12146,9 +14229,9 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t duk_tval tv_tmp; duk_double_t val; DUK__ASSERT_LEFT(8); - val = DUK_RAW_READ_DOUBLE_BE(p); - DUK_TVAL_SET_NUMBER_CHKFAST(&tv_tmp, val); - duk_push_tval(ctx, &tv_tmp); + val = DUK_RAW_READINC_DOUBLE_BE(p); + DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(&tv_tmp, val); + duk_push_tval(thr, &tv_tmp); break; } default: { @@ -12159,7 +14242,7 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t /* Load inner functions to value stack, but don't yet copy to buffer. */ for (n = count_funcs; n > 0; n--) { - p = duk__load_func(ctx, p, p_end); + p = duk__load_func(thr, p, p_end); if (p == NULL) { goto format_error; } @@ -12174,27 +14257,23 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * them afterwards. */ - h_data = (duk_hbuffer *) duk_get_hbuffer(ctx, idx_base + 1); - DUK_ASSERT(h_data != NULL); + h_data = (duk_hbuffer *) duk_known_hbuffer(thr, idx_base + 1); DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data)); - DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_fun, h_data); + DUK_HCOMPFUNC_SET_DATA(thr->heap, h_fun, h_data); DUK_HBUFFER_INCREF(thr, h_data); - tv1 = duk_get_tval(ctx, idx_base + 2); /* may be NULL if no constants or inner funcs */ + tv1 = duk_get_tval(thr, idx_base + 2); /* may be NULL if no constants or inner funcs */ DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL); q = fun_data; - if (count_const > 0) { - /* Explicit zero size check to avoid NULL 'tv1'. */ - DUK_MEMCPY((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const); - for (n = count_const; n > 0; n--) { - DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q); /* no side effects */ - q += sizeof(duk_tval); - } - tv1 += count_const; + duk_memcpy_unsafe((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const); + for (n = count_const; n > 0; n--) { + DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q); /* no side effects */ + q += sizeof(duk_tval); } + tv1 += count_const; - DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q); + DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q); for (n = count_funcs; n > 0; n--) { duk_hobject *h_obj; @@ -12208,108 +14287,142 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t q += sizeof(duk_hobject *); } - DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q); + DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q); /* The function object is now reachable and refcounts are fine, * so we can pop off all the temporaries. */ - DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(ctx, idx_base))); - duk_set_top(ctx, idx_base + 1); + DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(thr, idx_base))); + duk_set_top(thr, idx_base + 1); /* Setup function properties. */ - tmp32 = DUK_RAW_READ_U32_BE(p); - duk_push_u32(ctx, tmp32); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); + tmp32 = DUK_RAW_READINC_U32_BE(p); + duk_push_u32(thr, tmp32); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); - p = duk__load_string_raw(ctx, p); +#if defined(DUK_USE_FUNC_NAME_PROPERTY) + p = duk__load_string_raw(thr, p); /* -> [ func funcname ] */ + func_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; + DUK_ASSERT(func_env != NULL); + need_pop = 0; if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) { /* Original function instance/template had NAMEBINDING. * Must create a lexical environment on loading to allow * recursive functions like 'function foo() { foo(); }'. */ - duk_hobject *proto; + duk_hdecenv *new_env; - proto = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - (void) duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - proto); - duk_dup(ctx, -2); /* -> [ func funcname env funcname ] */ - duk_dup(ctx, idx_base); /* -> [ func funcname env funcname func ] */ - duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ func funcname env ] */ - duk_xdef_prop_stridx(ctx, idx_base, DUK_STRIDX_INT_LEXENV, DUK_PROPDESC_FLAGS_WC); - /* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it - * will be ignored anyway - */ + new_env = duk_hdecenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); + DUK_ASSERT(new_env != NULL); + DUK_ASSERT(new_env->thread == NULL); /* Closed. */ + DUK_ASSERT(new_env->varmap == NULL); + DUK_ASSERT(new_env->regbase_byteoff == 0); + DUK_HDECENV_ASSERT_VALID(new_env); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); + DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, func_env); + DUK_HOBJECT_INCREF(thr, func_env); + + func_env = (duk_hobject *) new_env; + + duk_push_hobject(thr, (duk_hobject *) new_env); + + duk_dup_m2(thr); /* -> [ func funcname env funcname ] */ + duk_dup(thr, idx_base); /* -> [ func funcname env funcname func ] */ + duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ func funcname env ] */ + + need_pop = 1; /* Need to pop env, but -after- updating h_fun and increfs. */ } - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); + DUK_ASSERT(func_env != NULL); + DUK_HCOMPFUNC_SET_LEXENV(thr->heap, h_fun, func_env); + DUK_HCOMPFUNC_SET_VARENV(thr->heap, h_fun, func_env); + DUK_HOBJECT_INCREF(thr, func_env); + DUK_HOBJECT_INCREF(thr, func_env); + if (need_pop) { + duk_pop(thr); + } + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); +#endif /* DUK_USE_FUNC_NAME_PROPERTY */ - p = duk__load_string_raw(ctx, p); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); +#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) + p = duk__load_string_raw(thr, p); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C); +#endif /* DUK_USE_FUNC_FILENAME_PROPERTY */ - duk_push_object(ctx); - duk_dup(ctx, -2); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* func.prototype.constructor = func */ - duk_compact(ctx, -1); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); + if (DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_fun)) { + /* Restore empty external .prototype only for constructable + * functions. The prototype object should inherit from + * Object.prototype. + */ + duk_push_object(thr); + DUK_ASSERT(!duk_is_bare_object(thr, -1)); + duk_dup_m2(thr); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* func.prototype.constructor = func */ + duk_compact_m1(thr); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); + } - p = duk__load_buffer_raw(ctx, p); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC); +#if defined(DUK_USE_PC2LINE) + p = duk__load_buffer_raw(thr, p); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC); +#endif /* DUK_USE_PC2LINE */ - duk_push_object(ctx); /* _Varmap */ + duk_push_bare_object(thr); /* _Varmap */ for (;;) { /* XXX: awkward */ - p = duk__load_string_raw(ctx, p); - if (duk_get_length(ctx, -1) == 0) { - duk_pop(ctx); + p = duk__load_string_raw(thr, p); + if (duk_get_length(thr, -1) == 0) { + duk_pop(thr); break; } - tmp32 = DUK_RAW_READ_U32_BE(p); - duk_push_u32(ctx, tmp32); - duk_put_prop(ctx, -3); + tmp32 = DUK_RAW_READINC_U32_BE(p); + duk_push_u32(thr, tmp32); + duk_put_prop(thr, -3); } - duk_compact(ctx, -1); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); + duk_compact_m1(thr); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); - duk_push_array(ctx); /* _Formals */ - for (arr_idx = 0; ; arr_idx++) { - /* XXX: awkward */ - p = duk__load_string_raw(ctx, p); - if (duk_get_length(ctx, -1) == 0) { - duk_pop(ctx); - break; + /* _Formals may have been missing in the original function, which is + * handled using a marker length. + */ + arr_limit = DUK_RAW_READINC_U32_BE(p); + if (arr_limit != DUK__NO_FORMALS) { + duk_push_bare_array(thr); /* _Formals */ + for (arr_idx = 0; arr_idx < arr_limit; arr_idx++) { + p = duk__load_string_raw(thr, p); + duk_put_prop_index(thr, -2, arr_idx); } - duk_put_prop_index(ctx, -2, arr_idx); + duk_compact_m1(thr); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); + } else { + DUK_DD(DUK_DDPRINT("no _Formals in dumped function")); } - duk_compact(ctx, -1); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); /* Return with final function pushed on stack top. */ - DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(ctx, -1))); - DUK_ASSERT_TOP(ctx, idx_base + 1); + DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(thr, -1))); + DUK_ASSERT_TOP(thr, idx_base + 1); return p; format_error: return NULL; } -DUK_EXTERNAL void duk_dump_function(duk_context *ctx) { - duk_hthread *thr; - duk_hcompiledfunction *func; +DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) { + duk_hcompfunc *func; duk_bufwriter_ctx bw_ctx_alloc; duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc; duk_uint8_t *p; - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); /* Bound functions don't have all properties so we'd either need to * lookup the non-bound target function or reject bound functions. - * For now, bound functions are rejected. + * For now, bound functions are rejected with TypeError. */ - func = duk_require_hcompiledfunction(ctx, -1); + func = duk_require_hcompfunc(thr, -1); DUK_ASSERT(func != NULL); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&func->obj)); + DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj)); /* Estimating the result size beforehand would be costly, so * start with a reasonable size and extend as needed. @@ -12317,26 +14430,22 @@ DUK_EXTERNAL void duk_dump_function(duk_context *ctx) { DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC); p = DUK_BW_GET_PTR(thr, bw_ctx); *p++ = DUK__SER_MARKER; - *p++ = DUK__SER_VERSION; - p = duk__dump_func(ctx, func, bw_ctx, p); + p = duk__dump_func(thr, func, bw_ctx, p); DUK_BW_SET_PTR(thr, bw_ctx, p); DUK_BW_COMPACT(thr, bw_ctx); - DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(ctx, -1))); + DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1))); - duk_remove(ctx, -2); /* [ ... func buf ] -> [ ... buf ] */ + duk_remove_m2(thr); /* [ ... func buf ] -> [ ... buf ] */ } -DUK_EXTERNAL void duk_load_function(duk_context *ctx) { - duk_hthread *thr; - duk_uint8_t *p_buf, *p, *p_end; +DUK_EXTERNAL void duk_load_function(duk_hthread *thr) { + const duk_uint8_t *p_buf, *p, *p_end; duk_size_t sz; - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; - DUK_UNREF(ctx); + DUK_ASSERT_API_ENTRY(thr); - p_buf = (duk_uint8_t *) duk_require_buffer(ctx, -1, &sz); + p_buf = (duk_uint8_t *) duk_require_buffer(thr, -1, &sz); DUK_ASSERT(p_buf != NULL); /* The caller is responsible for being sure that bytecode being loaded @@ -12345,548 +14454,476 @@ DUK_EXTERNAL void duk_load_function(duk_context *ctx) { * (instruction validation would be quite complex to implement). * * This signature check is the only sanity check for detecting - * accidental invalid inputs. The initial 0xFF byte ensures no - * ordinary string will be accepted by accident. + * accidental invalid inputs. The initial byte ensures no ordinary + * string or Symbol will be accepted by accident. */ p = p_buf; p_end = p_buf + sz; - if (sz < 2 || p[0] != DUK__SER_MARKER || p[1] != DUK__SER_VERSION) { + if (sz < 1 || p[0] != DUK__SER_MARKER) { goto format_error; } - p += 2; + p++; - p = duk__load_func(ctx, p, p_end); + p = duk__load_func(thr, p, p_end); if (p == NULL) { goto format_error; } - duk_remove(ctx, -2); /* [ ... buf func ] -> [ ... func ] */ + duk_remove_m2(thr); /* [ ... buf func ] -> [ ... func ] */ return; format_error: - DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED); + DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BYTECODE); + DUK_WO_NORETURN(return;); } -#undef DUK__SER_MARKER -#undef DUK__SER_VERSION -#undef DUK__SER_STRING -#undef DUK__SER_NUMBER -#undef DUK__BYTECODE_INITIAL_ALLOC - #else /* DUK_USE_BYTECODE_DUMP_SUPPORT */ -DUK_EXTERNAL void duk_dump_function(duk_context *ctx) { - DUK_ERROR_UNSUPPORTED_DEFMSG((duk_hthread *) ctx); +DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_load_function(duk_context *ctx) { - DUK_ERROR_UNSUPPORTED_DEFMSG((duk_hthread *) ctx); +DUK_EXTERNAL void duk_load_function(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); } #endif /* DUK_USE_BYTECODE_DUMP_SUPPORT */ -#line 1 "duk_api_call.c" + +/* automatic undefs */ +#undef DUK__ASSERT_LEFT +#undef DUK__BYTECODE_INITIAL_ALLOC +#undef DUK__NO_FORMALS +#undef DUK__SER_MARKER +#undef DUK__SER_NUMBER +#undef DUK__SER_STRING /* * Calls. * - * Protected variants should avoid ever throwing an error. + * Protected variants should avoid ever throwing an error. Must be careful + * to catch errors related to value stack manipulation and property lookup, + * not just the call itself. + * + * The only exception is when arguments are insane, e.g. nargs/nrets are out + * of bounds; in such cases an error is thrown for two reasons. First, we + * can't always respect the value stack input/output guarantees in such cases + * so the caller would end up with the value stack in an unexpected state. + * Second, an attempt to create an error might itself fail (although this + * could be avoided by pushing a preallocated object/string or a primitive + * value). */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +/* + * Helpers + */ + +struct duk__pcall_prop_args { + duk_idx_t obj_idx; + duk_idx_t nargs; + duk_small_uint_t call_flags; +}; +typedef struct duk__pcall_prop_args duk__pcall_prop_args; + +struct duk__pcall_method_args { + duk_idx_t nargs; + duk_small_uint_t call_flags; +}; +typedef struct duk__pcall_method_args duk__pcall_method_args; + +struct duk__pcall_args { + duk_idx_t nargs; + duk_small_uint_t call_flags; +}; +typedef struct duk__pcall_args duk__pcall_args; + +/* Compute and validate idx_func for a certain 'nargs' and 'other' + * parameter count (1 or 2, depending on whether 'this' binding is + * present). + */ +DUK_LOCAL duk_idx_t duk__call_get_idx_func(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) { + duk_idx_t idx_func; + + /* XXX: byte arithmetic? */ + + DUK_ASSERT(other >= 0); + + idx_func = duk_get_top(thr) - nargs - other; + if (DUK_UNLIKELY((idx_func | nargs) < 0)) { /* idx_func < 0 || nargs < 0; OR sign bits */ + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); + } + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); + return idx_func; +} + +/* Compute idx_func, assume index will be valid. This is a valid assumption + * for protected calls: nargs < 0 is checked explicitly and duk_safe_call() + * validates the argument count. + */ +DUK_LOCAL duk_idx_t duk__call_get_idx_func_unvalidated(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) { + duk_idx_t idx_func; + + /* XXX: byte arithmetic? */ + + DUK_ASSERT(nargs >= 0); + DUK_ASSERT(other >= 0); + + idx_func = duk_get_top(thr) - nargs - other; + DUK_ASSERT(idx_func >= 0); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); + return idx_func; +} /* Prepare value stack for a method call through an object property. * May currently throw an error e.g. when getting the property. */ -DUK_LOCAL void duk__call_prop_prep_stack(duk_context *ctx, duk_idx_t normalized_obj_index, duk_idx_t nargs) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_LOCAL void duk__call_prop_prep_stack(duk_hthread *thr, duk_idx_t normalized_obj_idx, duk_idx_t nargs) { + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(nargs >= 0); - DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_index=%ld, nargs=%ld, stacktop=%ld", - (long) normalized_obj_index, (long) nargs, (long) duk_get_top(ctx))); + DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_idx=%ld, nargs=%ld, stacktop=%ld", + (long) normalized_obj_idx, (long) nargs, (long) duk_get_top(thr))); /* [... key arg1 ... argN] */ /* duplicate key */ - duk_dup(ctx, -nargs - 1); /* Note: -nargs alone would fail for nargs == 0, this is OK */ - duk_get_prop(ctx, normalized_obj_index); + duk_dup(thr, -nargs - 1); /* Note: -nargs alone would fail for nargs == 0, this is OK */ + (void) duk_get_prop(thr, normalized_obj_idx); - DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(thr, -1))); + +#if defined(DUK_USE_VERBOSE_ERRORS) + if (DUK_UNLIKELY(!duk_is_callable(thr, -1))) { + duk_tval *tv_base; + duk_tval *tv_key; + + /* tv_targ is passed on stack top (at index -1). */ + tv_base = DUK_GET_TVAL_POSIDX(thr, normalized_obj_idx); + tv_key = DUK_GET_TVAL_NEGIDX(thr, -nargs - 2); + DUK_ASSERT(tv_base >= thr->valstack_bottom && tv_base < thr->valstack_top); + DUK_ASSERT(tv_key >= thr->valstack_bottom && tv_key < thr->valstack_top); + + duk_call_setup_propcall_error(thr, tv_base, tv_key); + } +#endif /* [... key arg1 ... argN func] */ - duk_replace(ctx, -nargs - 2); + duk_replace(thr, -nargs - 2); /* [... func arg1 ... argN] */ - duk_dup(ctx, normalized_obj_index); - duk_insert(ctx, -nargs - 1); + duk_dup(thr, normalized_obj_idx); + duk_insert(thr, -nargs - 1); /* [... func this arg1 ... argN] */ } -DUK_EXTERNAL void duk_call(duk_context *ctx, duk_idx_t nargs) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_call(duk_hthread *thr, duk_idx_t nargs) { duk_small_uint_t call_flags; duk_idx_t idx_func; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); - idx_func = duk_get_top(ctx) - nargs - 1; - if (idx_func < 0 || nargs < 0) { - /* note that we can't reliably pop anything here */ - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - } + idx_func = duk__call_get_idx_func(thr, nargs, 1); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - /* XXX: awkward; we assume there is space for this, overwrite - * directly instead? - */ - duk_push_undefined(ctx); - duk_insert(ctx, idx_func + 1); + duk_insert_undefined(thr, idx_func + 1); call_flags = 0; /* not protected, respect reclimit, not constructor */ - - duk_handle_call_unprotected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + duk_handle_call_unprotected(thr, idx_func, call_flags); } -DUK_EXTERNAL void duk_call_method(duk_context *ctx, duk_idx_t nargs) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_call_method(duk_hthread *thr, duk_idx_t nargs) { duk_small_uint_t call_flags; duk_idx_t idx_func; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); - idx_func = duk_get_top(ctx) - nargs - 2; /* must work for nargs <= 0 */ - if (idx_func < 0 || nargs < 0) { - /* note that we can't reliably pop anything here */ - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - } + idx_func = duk__call_get_idx_func(thr, nargs, 2); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); call_flags = 0; /* not protected, respect reclimit, not constructor */ - - duk_handle_call_unprotected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + duk_handle_call_unprotected(thr, idx_func, call_flags); } -DUK_EXTERNAL void duk_call_prop(duk_context *ctx, duk_idx_t obj_index, duk_idx_t nargs) { +DUK_EXTERNAL void duk_call_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) { /* * XXX: if duk_handle_call() took values through indices, this could be * made much more sensible. However, duk_handle_call() needs to fudge - * the 'this' and 'func' values to handle bound function chains, which - * is now done "in-place", so this is not a trivial change. + * the 'this' and 'func' values to handle bound functions, which is now + * done "in-place", so this is not a trivial change. */ - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj_index = duk_require_normalize_index(ctx, obj_index); /* make absolute */ - - duk__call_prop_prep_stack(ctx, obj_index, nargs); - - duk_call_method(ctx, nargs); -} - -DUK_EXTERNAL duk_int_t duk_pcall(duk_context *ctx, duk_idx_t nargs) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_small_uint_t call_flags; - duk_idx_t idx_func; - duk_int_t rc; - - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - - idx_func = duk_get_top(ctx) - nargs - 1; /* must work for nargs <= 0 */ - if (idx_func < 0 || nargs < 0) { - /* We can't reliably pop anything here because the stack input - * shape is incorrect. So we throw an error; if the caller has - * no catch point for this, a fatal error will occur. Another - * alternative would be to just return an error. But then the - * stack would be in an unknown state which might cause some - * very hard to diagnose problems later on. Also note that even - * if we did not throw an error here, the underlying call handler - * might STILL throw an out-of-memory error or some other internal - * fatal error. - */ - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return DUK_EXEC_ERROR; /* unreachable */ + obj_idx = duk_require_normalize_index(thr, obj_idx); /* make absolute */ + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return;); } - /* awkward; we assume there is space for this */ - duk_push_undefined(ctx); - duk_insert(ctx, idx_func + 1); + duk__call_prop_prep_stack(thr, obj_idx, nargs); - call_flags = 0; /* respect reclimit, not constructor */ - - rc = duk_handle_call_protected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ - - return rc; + duk_call_method(thr, nargs); } -DUK_EXTERNAL duk_int_t duk_pcall_method(duk_context *ctx, duk_idx_t nargs) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_small_uint_t call_flags; +DUK_LOCAL duk_ret_t duk__pcall_raw(duk_hthread *thr, void *udata) { + duk__pcall_args *args; duk_idx_t idx_func; - duk_int_t rc; + duk_int_t ret; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(udata != NULL); - idx_func = duk_get_top(ctx) - nargs - 2; /* must work for nargs <= 0 */ - if (idx_func < 0 || nargs < 0) { - /* See comments in duk_pcall(). */ - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return DUK_EXEC_ERROR; /* unreachable */ - } + args = (duk__pcall_args *) udata; + idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 1); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - call_flags = 0; /* respect reclimit, not constructor */ + duk_insert_undefined(thr, idx_func + 1); - rc = duk_handle_call_protected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags); + DUK_ASSERT(ret == 0); + DUK_UNREF(ret); - return rc; -} - -DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_context *ctx) { - duk_idx_t obj_index; - duk_idx_t nargs; - - /* Get the original arguments. Note that obj_index may be a relative - * index so the stack must have the same top when we use it. - */ - - DUK_ASSERT_CTX_VALID(ctx); - - obj_index = (duk_idx_t) duk_get_int(ctx, -2); - nargs = (duk_idx_t) duk_get_int(ctx, -1); - duk_pop_2(ctx); - - obj_index = duk_require_normalize_index(ctx, obj_index); /* make absolute */ - duk__call_prop_prep_stack(ctx, obj_index, nargs); - duk_call_method(ctx, nargs); return 1; } -DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_context *ctx, duk_idx_t obj_index, duk_idx_t nargs) { - /* - * Must be careful to catch errors related to value stack manipulation - * and property lookup, not just the call itself. - */ +DUK_EXTERNAL duk_int_t duk_pcall(duk_hthread *thr, duk_idx_t nargs) { + duk__pcall_args args; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - duk_push_idx(ctx, obj_index); - duk_push_idx(ctx, nargs); + args.nargs = nargs; + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return DUK_EXEC_ERROR;); + } + args.call_flags = 0; - /* Inputs: explicit arguments (nargs), +1 for key, +2 for obj_index/nargs passing. - * If the value stack does not contain enough args, an error is thrown; this matches - * behavior of the other protected call API functions. - */ - return duk_safe_call(ctx, duk__pcall_prop_raw, nargs + 1 + 2 /*nargs*/, 1 /*nrets*/); + return duk_safe_call(thr, duk__pcall_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); } -DUK_EXTERNAL duk_int_t duk_safe_call(duk_context *ctx, duk_safe_call_function func, duk_idx_t nargs, duk_idx_t nrets) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL duk_ret_t duk__pcall_method_raw(duk_hthread *thr, void *udata) { + duk__pcall_method_args *args; + duk_idx_t idx_func; + duk_int_t ret; + + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(udata != NULL); + + args = (duk__pcall_method_args *) udata; + + idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 2); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); + + ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags); + DUK_ASSERT(ret == 0); + DUK_UNREF(ret); + + return 1; +} + +DUK_INTERNAL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags) { + duk__pcall_method_args args; + + DUK_ASSERT_API_ENTRY(thr); + + args.nargs = nargs; + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return DUK_EXEC_ERROR;); + } + args.call_flags = call_flags; + + return duk_safe_call(thr, duk__pcall_method_raw, (void *) &args /*udata*/, nargs + 2 /*nargs*/, 1 /*nrets*/); +} + +DUK_EXTERNAL duk_int_t duk_pcall_method(duk_hthread *thr, duk_idx_t nargs) { + DUK_ASSERT_API_ENTRY(thr); + + return duk_pcall_method_flags(thr, nargs, 0); +} + +DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_hthread *thr, void *udata) { + duk__pcall_prop_args *args; + duk_idx_t obj_idx; + duk_int_t ret; + + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(udata != NULL); + + args = (duk__pcall_prop_args *) udata; + + obj_idx = duk_require_normalize_index(thr, args->obj_idx); /* make absolute */ + duk__call_prop_prep_stack(thr, obj_idx, args->nargs); + + ret = duk_handle_call_unprotected_nargs(thr, args->nargs, args->call_flags); + DUK_ASSERT(ret == 0); + DUK_UNREF(ret); + return 1; +} + +DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) { + duk__pcall_prop_args args; + + DUK_ASSERT_API_ENTRY(thr); + + args.obj_idx = obj_idx; + args.nargs = nargs; + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return DUK_EXEC_ERROR;); + } + args.call_flags = 0; + + return duk_safe_call(thr, duk__pcall_prop_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); +} + +DUK_EXTERNAL duk_int_t duk_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) { duk_int_t rc; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); - if (duk_get_top(ctx) < nargs || nrets < 0) { - /* See comments in duk_pcall(). */ - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return DUK_EXEC_ERROR; /* unreachable */ + /* nargs condition; fail if: top - bottom < nargs + * <=> top < bottom + nargs + * nrets condition; fail if: end - (top - nargs) < nrets + * <=> end - top + nargs < nrets + * <=> end + nargs < top + nrets + */ + /* XXX: check for any reserve? */ + + if (DUK_UNLIKELY((nargs | nrets) < 0 || /* nargs < 0 || nrets < 0; OR sign bits */ + thr->valstack_top < thr->valstack_bottom + nargs || /* nargs too large compared to top */ + thr->valstack_end + nargs < thr->valstack_top + nrets)) { /* nrets too large compared to reserve */ + DUK_D(DUK_DPRINT("not enough stack reserve for safe call or invalid arguments: " + "nargs=%ld < 0 (?), nrets=%ld < 0 (?), top=%ld < bottom=%ld + nargs=%ld (?), " + "end=%ld + nargs=%ld < top=%ld + nrets=%ld (?)", + (long) nargs, + (long) nrets, + (long) (thr->valstack_top - thr->valstack), + (long) (thr->valstack_bottom - thr->valstack), + (long) nargs, + (long) (thr->valstack_end - thr->valstack), + (long) nargs, + (long) (thr->valstack_top - thr->valstack), + (long) nrets)); + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return DUK_EXEC_ERROR;); } rc = duk_handle_safe_call(thr, /* thread */ func, /* func */ + udata, /* udata */ nargs, /* num_stack_args */ nrets); /* num_stack_res */ return rc; } -DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) { - /* - * There are two [[Construct]] operations in the specification: - * - * - E5 Section 13.2.2: for Function objects - * - E5 Section 15.3.4.5.2: for "bound" Function objects - * - * The chain of bound functions is resolved in Section 15.3.4.5.2, - * with arguments "piling up" until the [[Construct]] internal - * method is called on the final, actual Function object. Note - * that the "prototype" property is looked up *only* from the - * final object, *before* calling the constructor. - * - * Currently we follow the bound function chain here to get the - * "prototype" property value from the final, non-bound function. - * However, we let duk_handle_call() handle the argument "piling" - * when the constructor is called. The bound function chain is - * thus now processed twice. - * - * When constructing new Array instances, an unnecessary object is - * created and discarded now: the standard [[Construct]] creates an - * object, and calls the Array constructor. The Array constructor - * returns an Array instance, which is used as the result value for - * the "new" operation; the object created before the Array constructor - * call is discarded. - * - * This would be easy to fix, e.g. by knowing that the Array constructor - * will always create a replacement object and skip creating the fallback - * object in that case. - * - * Note: functions called via "new" need to know they are called as a - * constructor. For instance, built-in constructors behave differently - * depending on how they are called. - */ +DUK_EXTERNAL void duk_new(duk_hthread *thr, duk_idx_t nargs) { + duk_idx_t idx_func; - /* XXX: merge this with duk_js_call.c, as this function implements - * core semantics (or perhaps merge the two files altogether). - */ + DUK_ASSERT_API_ENTRY(thr); - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *proto; - duk_hobject *cons; - duk_hobject *fallback; - duk_idx_t idx_cons; - duk_small_uint_t call_flags; + idx_func = duk__call_get_idx_func(thr, nargs, 1); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - DUK_ASSERT_CTX_VALID(ctx); + duk_push_object(thr); /* default instance; internal proto updated by call handling */ + duk_insert(thr, idx_func + 1); - /* [... constructor arg1 ... argN] */ - - idx_cons = duk_require_normalize_index(ctx, -nargs - 1); - - DUK_DDD(DUK_DDDPRINT("top=%ld, nargs=%ld, idx_cons=%ld", - (long) duk_get_top(ctx), (long) nargs, (long) idx_cons)); - - /* XXX: code duplication */ - - /* - * Figure out the final, non-bound constructor, to get "prototype" - * property. - */ - - duk_dup(ctx, idx_cons); - for (;;) { - duk_tval *tv; - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - cons = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(cons != NULL); - if (!DUK_HOBJECT_IS_CALLABLE(cons) || !DUK_HOBJECT_HAS_CONSTRUCTABLE(cons)) { - /* Checking callability of the immediate target - * is important, same for constructability. - * Checking it for functions down the bound - * function chain is not strictly necessary - * because .bind() should normally reject them. - * But it's good to check anyway because it's - * technically possible to edit the bound function - * chain via internal keys. - */ - goto not_constructable; - } - if (!DUK_HOBJECT_HAS_BOUND(cons)) { - break; - } - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - /* Lightfuncs cannot be bound. */ - break; - } else { - /* Anything else is not constructable. */ - goto not_constructable; - } - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET); /* -> [... cons target] */ - duk_remove(ctx, -2); /* -> [... target] */ - } - DUK_ASSERT(duk_is_callable(ctx, -1)); - DUK_ASSERT(duk_is_lightfunc(ctx, -1) || - (duk_get_hobject(ctx, -1) != NULL && !DUK_HOBJECT_HAS_BOUND(duk_get_hobject(ctx, -1)))); - - /* [... constructor arg1 ... argN final_cons] */ - - /* - * Create "fallback" object to be used as the object instance, - * unless the constructor returns a replacement value. - * Its internal prototype needs to be set based on "prototype" - * property of the constructor. - */ - - duk_push_object(ctx); /* class Object, extensible */ - - /* [... constructor arg1 ... argN final_cons fallback] */ - - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE); - proto = duk_get_hobject(ctx, -1); - if (!proto) { - DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object " - "-> leave standard Object prototype as fallback prototype")); - } else { - DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value " - "-> set fallback prototype to that value: %!iO", (duk_heaphdr *) proto)); - fallback = duk_get_hobject(ctx, -2); - DUK_ASSERT(fallback != NULL); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto); - } - duk_pop(ctx); - - /* [... constructor arg1 ... argN final_cons fallback] */ - - /* - * Manipulate callstack for the call. - */ - - duk_dup_top(ctx); - duk_insert(ctx, idx_cons + 1); /* use fallback as 'this' value */ - duk_insert(ctx, idx_cons); /* also stash it before constructor, - * in case we need it (as the fallback value) - */ - duk_pop(ctx); /* pop final_cons */ - - - /* [... fallback constructor fallback(this) arg1 ... argN]; - * Note: idx_cons points to first 'fallback', not 'constructor'. - */ - - DUK_DDD(DUK_DDDPRINT("before call, idx_cons+1 (constructor) -> %!T, idx_cons+2 (fallback/this) -> %!T, " - "nargs=%ld, top=%ld", - (duk_tval *) duk_get_tval(ctx, idx_cons + 1), - (duk_tval *) duk_get_tval(ctx, idx_cons + 2), - (long) nargs, - (long) duk_get_top(ctx))); - - /* - * Call the constructor function (called in "constructor mode"). - */ - - call_flags = DUK_CALL_FLAG_CONSTRUCTOR_CALL; /* not protected, respect reclimit, is a constructor call */ - - duk_handle_call_unprotected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ - - /* [... fallback retval] */ - - DUK_DDD(DUK_DDDPRINT("constructor call finished, fallback=%!iT, retval=%!iT", - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); - - /* - * Determine whether to use the constructor return value as the created - * object instance or not. - */ - - if (duk_is_object(ctx, -1)) { - duk_remove(ctx, -2); - } else { - duk_pop(ctx); - } - - /* - * Augment created errors upon creation (not when they are thrown or - * rethrown). __FILE__ and __LINE__ are not desirable here; the call - * stack reflects the caller which is correct. - */ - -#ifdef DUK_USE_AUGMENT_ERROR_CREATE - duk_hthread_sync_currpc(thr); - duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); -#endif - - /* [... retval] */ - - return; - - not_constructable: - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONSTRUCTABLE); + duk_handle_call_unprotected(thr, idx_func, DUK_CALL_FLAG_CONSTRUCT); } -DUK_LOCAL duk_ret_t duk__pnew_helper(duk_context *ctx) { - duk_uint_t nargs; +DUK_LOCAL duk_ret_t duk__pnew_helper(duk_hthread *thr, void *udata) { + duk_idx_t nargs; - nargs = duk_to_uint(ctx, -1); - duk_pop(ctx); + DUK_ASSERT(udata != NULL); + nargs = *((duk_idx_t *) udata); - duk_new(ctx, nargs); + duk_new(thr, nargs); return 1; } -DUK_EXTERNAL duk_int_t duk_pnew(duk_context *ctx, duk_idx_t nargs) { +DUK_EXTERNAL duk_int_t duk_pnew(duk_hthread *thr, duk_idx_t nargs) { duk_int_t rc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* For now, just use duk_safe_call() to wrap duk_new(). We can't - * simply use a protected duk_handle_call() because there's post - * processing which might throw. It should be possible to ensure - * the post processing never throws (except in internal errors and - * out of memory etc which are always allowed) and then remove this - * wrapper. + * simply use a protected duk_handle_call() because pushing the + * default instance might throw. */ - duk_push_uint(ctx, nargs); - rc = duk_safe_call(ctx, duk__pnew_helper, nargs + 2 /*nargs*/, 1 /*nrets*/); + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return DUK_EXEC_ERROR;); + } + + rc = duk_safe_call(thr, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); return rc; } -DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_hthread *thr) { duk_activation *act; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(thr->callstack_top >= 0); + DUK_ASSERT_API_ENTRY(thr); - act = duk_hthread_get_current_activation(thr); - DUK_ASSERT(act != NULL); /* because callstack_top > 0 */ - return ((act->flags & DUK_ACT_FLAG_CONSTRUCT) != 0 ? 1 : 0); + act = thr->callstack_curr; + if (act != NULL) { + return ((act->flags & DUK_ACT_FLAG_CONSTRUCT) != 0 ? 1 : 0); + } + return 0; } -DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_require_constructor_call(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + + if (!duk_is_constructor_call(thr)) { + DUK_ERROR_TYPE(thr, DUK_STR_CONSTRUCT_ONLY); + DUK_WO_NORETURN(return;); + } +} + +DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_hthread *thr) { duk_activation *act; /* For user code this could just return 1 (strict) always * because all Duktape/C functions are considered strict, * and strict is also the default when nothing is running. * However, Duktape may call this function internally when - * the current activation is an Ecmascript function, so + * the current activation is an ECMAScript function, so * this cannot be replaced by a 'return 1' without fixing * the internal call sites. */ - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(thr->callstack_top >= 0); + DUK_ASSERT_API_ENTRY(thr); - act = duk_hthread_get_current_activation(thr); - if (act == NULL) { + act = thr->callstack_curr; + if (act != NULL) { + return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0); + } else { /* Strict by default. */ return 1; } - return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0); } /* * Duktape/C function magic */ -DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_hthread *thr) { duk_activation *act; duk_hobject *func; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(thr->callstack_top >= 0); + DUK_ASSERT_API_ENTRY(thr); - act = duk_hthread_get_current_activation(thr); + act = thr->callstack_curr; if (act) { func = DUK_ACT_GET_FUNC(act); if (!func) { @@ -12897,29 +14934,28 @@ DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) { } DUK_ASSERT(func != NULL); - if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) { - duk_hnativefunction *nf = (duk_hnativefunction *) func; + if (DUK_HOBJECT_IS_NATFUNC(func)) { + duk_hnatfunc *nf = (duk_hnatfunc *) func; return (duk_int_t) nf->magic; } } return 0; } -DUK_EXTERNAL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_int_t duk_get_magic(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); if (DUK_TVAL_IS_OBJECT(tv)) { h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_HAS_NATIVEFUNCTION(h)) { + if (!DUK_HOBJECT_HAS_NATFUNC(h)) { goto type_error; } - return (duk_int_t) ((duk_hnativefunction *) h)->magic; + return (duk_int_t) ((duk_hnatfunc *) h)->magic; } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { duk_small_uint_t lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); return (duk_int_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); @@ -12928,19 +14964,58 @@ DUK_EXTERNAL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t index) { /* fall through */ type_error: DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE); - return 0; + DUK_WO_NORETURN(return 0;); } -DUK_EXTERNAL void duk_set_magic(duk_context *ctx, duk_idx_t index, duk_int_t magic) { - duk_hnativefunction *nf; +DUK_EXTERNAL void duk_set_magic(duk_hthread *thr, duk_idx_t idx, duk_int_t magic) { + duk_hnatfunc *nf; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - nf = duk_require_hnativefunction(ctx, index); + nf = duk_require_hnatfunc(thr, idx); DUK_ASSERT(nf != NULL); nf->magic = (duk_int16_t) magic; } -#line 1 "duk_api_codec.c" + +/* + * Misc helpers + */ + +/* Resolve a bound function on value stack top to a non-bound target + * (leave other values as is). + */ +DUK_INTERNAL void duk_resolve_nonbound_function(duk_hthread *thr) { + duk_tval *tv; + + DUK_HTHREAD_ASSERT_VALID(thr); + + tv = DUK_GET_TVAL_NEGIDX(thr, -1); + if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h; + + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) { + duk_push_tval(thr, &((duk_hboundfunc *) (void *) h)->target); + duk_replace(thr, -2); +#if 0 + DUK_TVAL_SET_TVAL(tv, &((duk_hboundfunc *) h)->target); + DUK_TVAL_INCREF(thr, tv); + DUK_HOBJECT_DECREF_NORZ(thr, h); +#endif + /* Rely on Function.prototype.bind() on never creating a bound + * function whose target is not proper. This is now safe + * because the target is not even an internal property but a + * struct member. + */ + DUK_ASSERT(duk_is_lightfunc(thr, -1) || duk_is_callable(thr, -1)); + } + } + + /* Lightfuncs cannot be bound but are always callable and + * constructable. + */ +} /* * Encoding and decoding basic formats: hex, base64. * @@ -12949,386 +15024,626 @@ DUK_EXTERNAL void duk_set_magic(duk_context *ctx, duk_idx_t index, duk_int_t mag * Base-64: https://tools.ietf.org/html/rfc4648#section-4 */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +/* + * Misc helpers + */ /* Shared handling for encode/decode argument. Fast path handling for * buffer and string values because they're the most common. In particular, - * avoid creating a temporary string or buffer when possible. + * avoid creating a temporary string or buffer when possible. Return value + * is guaranteed to be non-NULL, even for zero length input. */ -DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) { - DUK_ASSERT(duk_is_valid_index(ctx, index)); /* checked by caller */ - if (duk_is_buffer(ctx, index)) { - return (const duk_uint8_t *) duk_get_buffer(ctx, index, out_len); +DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + const void *def_ptr = (const void *) out_len; /* Any non-NULL pointer will do. */ + const void *ptr; + duk_bool_t isbuffer; + + DUK_ASSERT(out_len != NULL); + DUK_ASSERT(def_ptr != NULL); + DUK_ASSERT(duk_is_valid_index(thr, idx)); /* checked by caller */ + + ptr = (const void *) duk_get_buffer_data_raw(thr, idx, out_len, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, &isbuffer); + if (isbuffer) { + DUK_ASSERT(ptr != NULL || *out_len == 0U); + if (DUK_UNLIKELY(ptr == NULL)) { + ptr = def_ptr; + } + DUK_ASSERT(ptr != NULL); } else { - return (const duk_uint8_t *) duk_to_lstring(ctx, index, out_len); + /* For strings a non-NULL pointer is always guaranteed because + * at least a NUL will be present. + */ + ptr = (const void *) duk_to_lstring(thr, idx, out_len); + DUK_ASSERT(ptr != NULL); } + DUK_ASSERT(ptr != NULL); + return (const duk_uint8_t *) ptr; } +/* + * Base64 + */ + +#if defined(DUK_USE_BASE64_SUPPORT) +/* Bytes emitted for number of padding characters in range [0,4]. */ +DUK_LOCAL const duk_int8_t duk__base64_decode_nequal_step[5] = { + 3, /* #### -> 24 bits, emit 3 bytes */ + 2, /* ###= -> 18 bits, emit 2 bytes */ + 1, /* ##== -> 12 bits, emit 1 byte */ + -1, /* #=== -> 6 bits, error */ + 0, /* ==== -> 0 bits, emit 0 bytes */ +}; + #if defined(DUK_USE_BASE64_FASTPATH) -DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) { +DUK_LOCAL const duk_uint8_t duk__base64_enctab_fast[64] = { + 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU, 0x50U, /* A...P */ + 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U, 0x59U, 0x5aU, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, /* Q...f */ + 0x67U, 0x68U, 0x69U, 0x6aU, 0x6bU, 0x6cU, 0x6dU, 0x6eU, 0x6fU, 0x70U, 0x71U, 0x72U, 0x73U, 0x74U, 0x75U, 0x76U, /* g...v */ + 0x77U, 0x78U, 0x79U, 0x7aU, 0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U, 0x38U, 0x39U, 0x2bU, 0x2fU /* w.../ */ +}; +#endif /* DUK_USE_BASE64_FASTPATH */ + +#if defined(DUK_USE_BASE64_FASTPATH) +/* Decode table for one byte of input: + * -1 = allowed whitespace + * -2 = padding + * -3 = error + * 0...63 decoded bytes + */ +DUK_LOCAL const duk_int8_t duk__base64_dectab_fast[256] = { + -3, -3, -3, -3, -3, -3, -3, -3, -3, -1, -1, -3, -3, -1, -3, -3, /* 0x00...0x0f */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x10...0x1f */ + -1, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, 62, -3, -3, -3, 63, /* 0x20...0x2f */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -3, -3, -3, -2, -3, -3, /* 0x30...0x3f */ + -3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40...0x4f */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -3, -3, -3, -3, -3, /* 0x50...0x5f */ + -3, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60...0x6f */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -3, -3, -3, -3, -3, /* 0x70...0x7f */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x80...0x8f */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0x90...0x9f */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xa0...0xaf */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xb0...0xbf */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xc0...0xcf */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xd0...0xdf */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, /* 0xe0...0xef */ + -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3 /* 0xf0...0xff */ +}; +#endif /* DUK_USE_BASE64_FASTPATH */ + +#if defined(DUK_USE_BASE64_FASTPATH) +DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_3(const duk_uint8_t *src, duk_uint8_t *dst) { duk_uint_t t; - duk_size_t n_full, n_full3, n_final; - const duk_uint8_t *src_end_fast; - n_full = srclen / 3; /* full 3-byte -> 4-char conversions */ - n_full3 = n_full * 3; - n_final = srclen - n_full3; - DUK_ASSERT_DISABLE(n_final >= 0); - DUK_ASSERT(n_final <= 2); + t = (duk_uint_t) src[0]; + t = (t << 8) + (duk_uint_t) src[1]; + t = (t << 8) + (duk_uint_t) src[2]; - src_end_fast = src + n_full3; - while (DUK_UNLIKELY(src != src_end_fast)) { - t = (duk_uint_t) (*src++); - t = (t << 8) + (duk_uint_t) (*src++); - t = (t << 8) + (duk_uint_t) (*src++); + dst[0] = duk__base64_enctab_fast[t >> 18]; + dst[1] = duk__base64_enctab_fast[(t >> 12) & 0x3fU]; + dst[2] = duk__base64_enctab_fast[(t >> 6) & 0x3fU]; + dst[3] = duk__base64_enctab_fast[t & 0x3fU]; - *dst++ = duk_base64_enctab[t >> 18]; - *dst++ = duk_base64_enctab[(t >> 12) & 0x3f]; - *dst++ = duk_base64_enctab[(t >> 6) & 0x3f]; - *dst++ = duk_base64_enctab[t & 0x3f]; - -#if 0 /* Tested: not faster on x64 */ - /* aaaaaabb bbbbcccc ccdddddd */ - dst[0] = duk_base64_enctab[(src[0] >> 2) & 0x3f]; - dst[1] = duk_base64_enctab[((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0f)]; - dst[2] = duk_base64_enctab[((src[1] << 2) & 0x3f) | ((src[2] >> 6) & 0x03)]; - dst[3] = duk_base64_enctab[src[2] & 0x3f]; - src += 3; dst += 4; +#if 0 + /* Tested: not faster on x64, most likely due to aliasing between + * output and input index computation. + */ + /* aaaaaabb bbbbcccc ccdddddd */ + dst[0] = duk__base64_enctab_fast[(src[0] >> 2) & 0x3fU]; + dst[1] = duk__base64_enctab_fast[((src[0] << 4) & 0x30U) | ((src[1] >> 4) & 0x0fU)]; + dst[2] = duk__base64_enctab_fast[((src[1] << 2) & 0x3fU) | ((src[2] >> 6) & 0x03U)]; + dst[3] = duk__base64_enctab_fast[src[2] & 0x3fU]; #endif +} + +DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_2(const duk_uint8_t *src, duk_uint8_t *dst) { + duk_uint_t t; + + t = (duk_uint_t) src[0]; + t = (t << 8) + (duk_uint_t) src[1]; + dst[0] = duk__base64_enctab_fast[t >> 10]; /* XXXXXX-- -------- */ + dst[1] = duk__base64_enctab_fast[(t >> 4) & 0x3fU]; /* ------XX XXXX---- */ + dst[2] = duk__base64_enctab_fast[(t << 2) & 0x3fU]; /* -------- ----XXXX */ + dst[3] = DUK_ASC_EQUALS; +} + +DUK_LOCAL DUK_ALWAYS_INLINE void duk__base64_encode_fast_1(const duk_uint8_t *src, duk_uint8_t *dst) { + duk_uint_t t; + + t = (duk_uint_t) src[0]; + dst[0] = duk__base64_enctab_fast[t >> 2]; /* XXXXXX-- */ + dst[1] = duk__base64_enctab_fast[(t << 4) & 0x3fU]; /* ------XX */ + dst[2] = DUK_ASC_EQUALS; + dst[3] = DUK_ASC_EQUALS; +} + +DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) { + duk_size_t n; + const duk_uint8_t *p; + duk_uint8_t *q; + + n = srclen; + p = src; + q = dst; + + if (n >= 16U) { + /* Fast path, unrolled by 4, allows interleaving. Process + * 12-byte input chunks which encode to 16-char output chunks. + * Only enter when at least one block is emitted (avoids div+mul + * for short inputs too). + */ + const duk_uint8_t *p_end_fast; + + p_end_fast = p + ((n / 12U) * 12U); + DUK_ASSERT(p_end_fast >= p + 12); + do { + duk__base64_encode_fast_3(p, q); + duk__base64_encode_fast_3(p + 3, q + 4); + duk__base64_encode_fast_3(p + 6, q + 8); + duk__base64_encode_fast_3(p + 9, q + 12); + p += 12; + q += 16; + } while (DUK_LIKELY(p != p_end_fast)); + + DUK_ASSERT(src + srclen >= p); + n = (duk_size_t) (src + srclen - p); + DUK_ASSERT(n < 12U); } - switch (n_final) { - /* case 0: nop */ - case 1: { - /* XX== */ - t = (duk_uint_t) (*src++); - *dst++ = duk_base64_enctab[t >> 2]; /* XXXXXX-- */ - *dst++ = duk_base64_enctab[(t << 4) & 0x3f]; /* ------XX */ - *dst++ = DUK_ASC_EQUALS; - *dst++ = DUK_ASC_EQUALS; - break; - } - case 2: { - /* XXX= */ - t = (duk_uint_t) (*src++); - t = (t << 8) + (duk_uint_t) (*src++); - *dst++ = duk_base64_enctab[t >> 10]; /* XXXXXX-- -------- */ - *dst++ = duk_base64_enctab[(t >> 4) & 0x3f]; /* ------XX XXXX---- */ - *dst++ = duk_base64_enctab[(t << 2) & 0x3f]; /* -------- ----XXXX */ - *dst++ = DUK_ASC_EQUALS; - break; + /* Remainder. */ + while (n >= 3U) { + duk__base64_encode_fast_3(p, q); + p += 3; + q += 4; + n -= 3U; } + DUK_ASSERT(n == 0U || n == 1U || n == 2U); + if (n == 1U) { + duk__base64_encode_fast_1(p, q); +#if 0 /* Unnecessary. */ + p += 1; + q += 4; + n -= 1U; +#endif + } else if (n == 2U) { + duk__base64_encode_fast_2(p, q); +#if 0 /* Unnecessary. */ + p += 2; + q += 4; + n -= 2U; +#endif + } else { + DUK_ASSERT(n == 0U); /* nothing to do */ + ; } } #else /* DUK_USE_BASE64_FASTPATH */ DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) { - duk_small_uint_t i, snip; - duk_uint_t t; - duk_uint_fast8_t x, y; - const duk_uint8_t *src_end; + duk_small_uint_t i, npad; + duk_uint_t t, x, y; + const duk_uint8_t *p; + const duk_uint8_t *p_end; + duk_uint8_t *q; - src_end = src + srclen; + p = src; + p_end = src + srclen; + q = dst; + npad = 0U; - while (src < src_end) { - /* read 3 bytes into 't', padded by zero */ - snip = 4; + while (p < p_end) { + /* Read 3 bytes into 't', padded by zero. */ t = 0; for (i = 0; i < 3; i++) { t = t << 8; - if (src >= src_end) { - snip--; + if (p < p_end) { + t += (duk_uint_t) (*p++); } else { - t += (duk_uint_t) (*src++); + /* This only happens on the last loop and we're + * guaranteed to exit on the next loop. + */ + npad++; } } + DUK_ASSERT(npad <= 2U); - /* - * Missing bytes snip base64 example - * 0 4 XXXX - * 1 3 XXX= - * 2 2 XX== + /* Emit 4 encoded characters. If npad > 0, some of the + * chars will be incorrect (zero bits) but we fix up the + * padding after the loop. A straightforward 64-byte + * lookup would be faster and cleaner, but this is shorter. */ - - DUK_ASSERT(snip >= 2 && snip <= 4); - for (i = 0; i < 4; i++) { - x = (duk_uint_fast8_t) ((t >> 18) & 0x3f); + x = ((t >> 18) & 0x3fU); t = t << 6; - /* A straightforward 64-byte lookup would be faster - * and cleaner, but this is shorter. - */ - if (i >= snip) { - y = '='; - } else if (x <= 25) { - y = x + 'A'; - } else if (x <= 51) { - y = x - 26 + 'a'; - } else if (x <= 61) { - y = x - 52 + '0'; - } else if (x == 62) { - y = '+'; + if (x <= 51U) { + if (x <= 25) { + y = x + DUK_ASC_UC_A; + } else { + y = x - 26 + DUK_ASC_LC_A; + } } else { - y = '/'; + if (x <= 61U) { + y = x - 52 + DUK_ASC_0; + } else if (x == 62) { + y = DUK_ASC_PLUS; + } else { + DUK_ASSERT(x == 63); + y = DUK_ASC_SLASH; + } } - *dst++ = (duk_uint8_t) y; + *q++ = (duk_uint8_t) y; } } + + /* Handle padding by rewriting 0-2 bogus characters at the end. + * + * Missing bytes npad base64 example + * 0 0 #### + * 1 1 ###= + * 2 2 ##== + */ + DUK_ASSERT(npad <= 2U); + while (npad > 0U) { + *(q - npad) = DUK_ASC_EQUALS; + npad--; + } } #endif /* DUK_USE_BASE64_FASTPATH */ #if defined(DUK_USE_BASE64_FASTPATH) DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) { duk_int_t x; - duk_int_t t; + duk_uint_t t; duk_small_uint_t n_equal; - duk_small_uint_t n_chars; - const duk_uint8_t *src_end; - const duk_uint8_t *src_end_safe; + duk_int8_t step; + const duk_uint8_t *p; + const duk_uint8_t *p_end; + const duk_uint8_t *p_end_safe; + duk_uint8_t *q; - src_end = src + srclen; - src_end_safe = src_end - 4; /* if 'src < src_end_safe', safe to read 4 bytes */ + DUK_ASSERT(src != NULL); /* Required by pointer arithmetic below, which fails for NULL. */ - /* Innermost fast path processes 4 valid base-64 characters at a time - * but bails out on whitespace, padding chars ('=') and invalid chars. - * Once the slow path segment has been processed, we return to the - * inner fast path again. This handles e.g. base64 with newlines - * reasonably well because the majority of a line is in the fast path. + p = src; + p_end = src + srclen; + p_end_safe = p_end - 8; /* If 'src <= src_end_safe', safe to read 8 bytes. */ + q = dst; + + /* Alternate between a fast path which processes clean groups with no + * padding or whitespace, and a slow path which processes one arbitrary + * group and then re-enters the fast path. This handles e.g. base64 + * with newlines reasonably well because the majority of a line is in + * the fast path. */ for (;;) { - /* Fast path, handle units with just actual encoding characters. */ + /* Fast path, on each loop handle two 4-char input groups. + * If both are clean, emit 6 bytes and continue. If first + * is clean, emit 3 bytes and drop out; otherwise emit + * nothing and drop out. This approach could be extended to + * more groups per loop, but for inputs with e.g. periodic + * newlines (which are common) it might not be an improvement. + */ + while (DUK_LIKELY(p <= p_end_safe)) { + duk_int_t t1, t2; - while (src <= src_end_safe) { - /* The lookup byte is intentionally sign extended to (at least) - * 32 bits and then ORed. This ensures that is at least 1 byte - * is negative, the highest bit of 't' will be set at the end - * and we don't need to check every byte. + /* The lookup byte is intentionally sign extended to + * (at least) 32 bits and then ORed. This ensures + * that is at least 1 byte is negative, the highest + * bit of the accumulator will be set at the end and + * we don't need to check every byte. + * + * Read all input bytes first before writing output + * bytes to minimize aliasing. */ - DUK_DDD(DUK_DDDPRINT("fast loop: src=%p, src_end_safe=%p, src_end=%p", - (const void *) src, (const void *) src_end_safe, (const void *) src_end)); + DUK_DDD(DUK_DDDPRINT("fast loop: p=%p, p_end_safe=%p, p_end=%p", + (const void *) p, (const void *) p_end_safe, (const void *) p_end)); - t = (duk_int_t) duk_base64_dectab[*src++]; - t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++]; - t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++]; - t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++]; + t1 = (duk_int_t) duk__base64_dectab_fast[p[0]]; + t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[1]]; + t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[2]]; + t1 = (duk_int_t) ((duk_uint_t) t1 << 6) | (duk_int_t) duk__base64_dectab_fast[p[3]]; - if (DUK_UNLIKELY(t < 0)) { - DUK_DDD(DUK_DDDPRINT("fast loop unit was not clean, process one slow path unit")); - src -= 4; + t2 = (duk_int_t) duk__base64_dectab_fast[p[4]]; + t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[5]]; + t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[6]]; + t2 = (duk_int_t) ((duk_uint_t) t2 << 6) | (duk_int_t) duk__base64_dectab_fast[p[7]]; + + q[0] = (duk_uint8_t) (((duk_uint_t) t1 >> 16) & 0xffU); + q[1] = (duk_uint8_t) (((duk_uint_t) t1 >> 8) & 0xffU); + q[2] = (duk_uint8_t) ((duk_uint_t) t1 & 0xffU); + + q[3] = (duk_uint8_t) (((duk_uint_t) t2 >> 16) & 0xffU); + q[4] = (duk_uint8_t) (((duk_uint_t) t2 >> 8) & 0xffU); + q[5] = (duk_uint8_t) ((duk_uint_t) t2 & 0xffU); + + /* Optimistic check using one branch. */ + if (DUK_LIKELY((t1 | t2) >= 0)) { + p += 8; + q += 6; + } else if (t1 >= 0) { + DUK_DDD(DUK_DDDPRINT("fast loop first group was clean, second was not, process one slow path group")); + DUK_ASSERT(t2 < 0); + p += 4; + q += 3; + break; + } else { + DUK_DDD(DUK_DDDPRINT("fast loop first group was not clean, second does not matter, process one slow path group")); + DUK_ASSERT(t1 < 0); break; } + } /* fast path */ - DUK_ASSERT(t <= 0xffffffL); - DUK_ASSERT((t >> 24) == 0); - *dst++ = (duk_uint8_t) (t >> 16); - *dst++ = (duk_uint8_t) ((t >> 8) & 0xff); - *dst++ = (duk_uint8_t) (t & 0xff); - } - - /* Handle one slow path unit (or finish if we're done). */ - - n_equal = 0; - n_chars = 0; - t = 0; - for (;;) { - DUK_DDD(DUK_DDDPRINT("slow loop: src=%p, src_end=%p, n_chars=%ld, n_equal=%ld, t=%ld", - (const void *) src, (const void *) src_end, (long) n_chars, (long) n_equal, (long) t)); - - if (DUK_UNLIKELY(src >= src_end)) { - goto done; /* two level break */ - } - - x = duk_base64_dectab[*src++]; - if (DUK_UNLIKELY(x < 0)) { - if (x == -2) { - continue; /* allowed ascii whitespace */ - } else if (x == -3) { - n_equal++; - t <<= 6; - } else { - DUK_ASSERT(x == -1); - goto error; - } - } else { - DUK_ASSERT(x >= 0 && x <= 63); - if (n_equal > 0) { - /* Don't allow actual chars after equal sign. */ - goto error; - } - t = (t << 6) + x; - } - - if (DUK_UNLIKELY(n_chars == 3)) { - /* Emit 3 bytes and backtrack if there was padding. There's - * always space for the whole 3 bytes so no check needed. - */ - DUK_ASSERT(t <= 0xffffffL); - DUK_ASSERT((t >> 24) == 0); - *dst++ = (duk_uint8_t) (t >> 16); - *dst++ = (duk_uint8_t) ((t >> 8) & 0xff); - *dst++ = (duk_uint8_t) (t & 0xff); - - if (DUK_UNLIKELY(n_equal > 0)) { - DUK_ASSERT(n_equal <= 4); - - /* There may be whitespace between the equal signs. */ - if (n_equal == 1) { - /* XXX= */ - dst -= 1; - } else if (n_equal == 2) { - /* XX== */ - dst -= 2; - } else { - goto error; /* invalid padding */ - } - - /* Continue parsing after padding, allows concatenated, - * padded base64. - */ - } - break; /* back to fast loop */ - } else { - n_chars++; - } - } - } - done: - DUK_DDD(DUK_DDDPRINT("done; src=%p, src_end=%p, n_chars=%ld", - (const void *) src, (const void *) src_end, (long) n_chars)); - - DUK_ASSERT(src == src_end); - - if (n_chars != 0) { - /* Here we'd have the option of decoding unpadded base64 - * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not - * accepted. + /* Slow path step 1: try to scan a 4-character encoded group, + * end-of-input, or start-of-padding. We exit with: + * 1. n_chars == 4: full group, no padding, no end-of-input. + * 2. n_chars < 4: partial group (may also be 0), encountered + * padding or end of input. + * + * The accumulator is initialized to 1; this allows us to detect + * a full group by comparing >= 0x1000000 without an extra + * counter variable. */ - goto error; - } + t = 1UL; + for (;;) { + DUK_DDD(DUK_DDDPRINT("slow loop: p=%p, p_end=%p, t=%lu", + (const void *) p, (const void *) p_end, (unsigned long) t)); - *out_dst_final = dst; + if (DUK_LIKELY(p < p_end)) { + x = duk__base64_dectab_fast[*p++]; + if (DUK_LIKELY(x >= 0)) { + DUK_ASSERT(x >= 0 && x <= 63); + t = (t << 6) + (duk_uint_t) x; + if (t >= 0x1000000UL) { + break; + } + } else if (x == -1) { + continue; /* allowed ascii whitespace */ + } else if (x == -2) { + p--; + break; /* start of padding */ + } else { + DUK_ASSERT(x == -3); + goto decode_error; + } + } else { + break; /* end of input */ + } + } /* slow path step 1 */ + + /* Complete the padding by simulating pad characters, + * regardless of actual input padding chars. + */ + n_equal = 0; + while (t < 0x1000000UL) { + t = (t << 6) + 0U; + n_equal++; + } + + /* Slow path step 2: deal with full/partial group, padding, + * etc. Note that for num chars in [0,3] we intentionally emit + * 3 bytes but don't step forward that much, buffer space is + * guaranteed in setup. + * + * num chars: + * 0 #### no output (= step 0) + * 1 #=== reject, 6 bits of data + * 2 ##== 12 bits of data, output 1 byte (= step 1) + * 3 ###= 18 bits of data, output 2 bytes (= step 2) + * 4 #### 24 bits of data, output 3 bytes (= step 3) + */ + q[0] = (duk_uint8_t) ((t >> 16) & 0xffU); + q[1] = (duk_uint8_t) ((t >> 8) & 0xffU); + q[2] = (duk_uint8_t) (t & 0xffU); + + DUK_ASSERT(n_equal <= 4); + step = duk__base64_decode_nequal_step[n_equal]; + if (DUK_UNLIKELY(step < 0)) { + goto decode_error; + } + q += step; + + /* Slow path step 3: read and ignore padding and whitespace + * until (a) next non-padding and non-whitespace character + * after which we resume the fast path, or (b) end of input. + * This allows us to accept missing, partial, full, and extra + * padding cases uniformly. We also support concatenated + * base-64 documents because we resume scanning afterwards. + * + * Note that to support concatenated documents well, the '=' + * padding found inside the input must also allow for 'extra' + * padding. For example, 'Zm===' decodes to 'f' and has one + * extra padding char. So, 'Zm===Zm' should decode 'ff', even + * though the standard break-up would be 'Zm==' + '=Zm' which + * doesn't make sense. + * + * We also accept prepended padding like '==Zm9', because it + * is equivalent to an empty document with extra padding ('==') + * followed by a valid document. + */ + + for (;;) { + if (DUK_UNLIKELY(p >= p_end)) { + goto done; + } + x = duk__base64_dectab_fast[*p++]; + if (x == -1 || x == -2) { + ; /* padding or whitespace, keep eating */ + } else { + p--; + break; /* backtrack and go back to fast path, even for -1 */ + } + } /* slow path step 3 */ + } /* outer fast+slow path loop */ + + done: + DUK_DDD(DUK_DDDPRINT("done; p=%p, p_end=%p", + (const void *) p, (const void *) p_end)); + + DUK_ASSERT(p == p_end); + + *out_dst_final = q; return 1; - error: + decode_error: return 0; } #else /* DUK_USE_BASE64_FASTPATH */ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) { - duk_uint_t t; - duk_uint_fast8_t x, y; - duk_small_uint_t group_idx; - duk_small_uint_t n_equal; - const duk_uint8_t *src_end; + duk_uint_t t, x; + duk_int_t y; + duk_int8_t step; + const duk_uint8_t *p; + const duk_uint8_t *p_end; + duk_uint8_t *q; + /* 0x09, 0x0a, or 0x0d */ + duk_uint32_t mask_white = (1U << 9) | (1U << 10) | (1U << 13); - src_end = src + srclen; - t = 0; - group_idx = 0; - n_equal = 0; + /* 't' tracks progress of the decoded group: + * + * t == 1 no valid chars yet + * t >= 0x40 1x6 = 6 bits shifted in + * t >= 0x1000 2x6 = 12 bits shifted in + * t >= 0x40000 3x6 = 18 bits shifted in + * t >= 0x1000000 4x6 = 24 bits shifted in + * + * By initializing t=1 there's no need for a separate counter for + * the number of characters found so far. + */ + p = src; + p_end = src + srclen; + q = dst; + t = 1UL; - while (src < src_end) { - x = *src++; + for (;;) { + duk_small_uint_t n_equal; - if (x >= 'A' && x <= 'Z') { - y = x - 'A' + 0; - } else if (x >= 'a' && x <= 'z') { - y = x - 'a' + 26; - } else if (x >= '0' && x <= '9') { - y = x - '0' + 52; - } else if (x == '+') { - y = 62; - } else if (x == '/') { - y = 63; - } else if (x == '=') { - /* We don't check the zero padding bytes here right now - * (that they're actually zero). This seems to be common - * behavior for base-64 decoders. + DUK_ASSERT(t >= 1U); + if (p >= p_end) { + /* End of input: if input exists, treat like + * start of padding, finish the block, then + * re-enter here to see we're done. */ - - n_equal++; - t <<= 6; /* shift in zeroes */ - goto skip_add; - } else if (x == 0x09 || x == 0x0a || x == 0x0d || x == 0x20) { - /* allow basic ASCII whitespace */ - continue; - } else { - goto error; - } - - if (n_equal > 0) { - /* Don't allow mixed padding and actual chars. */ - goto error; - } - t = (t << 6) + y; - skip_add: - - if (group_idx == 3) { - /* output 3 bytes from 't' */ - *dst++ = (duk_uint8_t) ((t >> 16) & 0xff); - *dst++ = (duk_uint8_t) ((t >> 8) & 0xff); - *dst++ = (duk_uint8_t) (t & 0xff); - - if (DUK_UNLIKELY(n_equal > 0)) { - /* Backtrack. */ - DUK_ASSERT(n_equal <= 4); - if (n_equal == 1) { - dst -= 1; - } else if (n_equal == 2) { - dst -= 2; - } else { - goto error; /* invalid padding */ - } - - /* Here we can choose either to end parsing and ignore - * whatever follows, or to continue parsing in case - * multiple (possibly padded) base64 strings have been - * concatenated. Currently, keep on parsing. - */ - n_equal = 0; + if (t == 1U) { + break; + } else { + goto simulate_padding; } - - t = 0; - group_idx = 0; - } else { - group_idx++; } - } - if (group_idx != 0) { - /* Here we'd have the option of decoding unpadded base64 - * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not - * accepted. + x = *p++; + + if (x >= 0x41U) { + /* Valid: a-z and A-Z. */ + DUK_ASSERT(x >= 0x41U && x <= 0xffU); + if (x >= 0x61U && x <= 0x7aU) { + y = (duk_int_t) x - 0x61 + 26; + } else if (x <= 0x5aU) { + y = (duk_int_t) x - 0x41; + } else { + goto decode_error; + } + } else if (x >= 0x30U) { + /* Valid: 0-9 and =. */ + DUK_ASSERT(x >= 0x30U && x <= 0x40U); + if (x <= 0x39U) { + y = (duk_int_t) x - 0x30 + 52; + } else if (x == 0x3dU) { + /* Skip padding and whitespace unless we're in the + * middle of a block. Otherwise complete group by + * simulating shifting in the correct padding. + */ + if (t == 1U) { + continue; + } + goto simulate_padding; + } else { + goto decode_error; + } + } else if (x >= 0x20U) { + /* Valid: +, /, and 0x20 whitespace. */ + DUK_ASSERT(x >= 0x20U && x <= 0x2fU); + if (x == 0x2bU) { + y = 62; + } else if (x == 0x2fU) { + y = 63; + } else if (x == 0x20U) { + continue; + } else { + goto decode_error; + } + } else { + /* Valid: whitespace. */ + duk_uint32_t m; + DUK_ASSERT(x < 0x20U); /* 0x00 to 0x1f */ + m = (1U << x); + if (mask_white & m) { + /* Allow basic ASCII whitespace. */ + continue; + } else { + goto decode_error; + } + } + + DUK_ASSERT(y >= 0 && y <= 63); + t = (t << 6) + (duk_uint_t) y; + if (t < 0x1000000UL) { + continue; + } + /* fall through; no padding will be added */ + + simulate_padding: + n_equal = 0; + while (t < 0x1000000UL) { + t = (t << 6) + 0U; + n_equal++; + } + + /* Output 3 bytes from 't' and advance as needed. */ + q[0] = (duk_uint8_t) ((t >> 16) & 0xffU); + q[1] = (duk_uint8_t) ((t >> 8) & 0xffU); + q[2] = (duk_uint8_t) (t & 0xffU); + + DUK_ASSERT(n_equal <= 4U); + step = duk__base64_decode_nequal_step[n_equal]; + if (step < 0) { + goto decode_error; + } + q += step; + + /* Re-enter loop. The actual padding characters are skipped + * by the main loop. This handles cases like missing, partial, + * full, and extra padding, and allows parsing of concatenated + * documents (with extra padding) like: Zm===Zm. Also extra + * prepended padding is accepted: ===Zm9v. */ - goto error; + t = 1U; } + DUK_ASSERT(t == 1UL); - *out_dst_final = dst; + *out_dst_final = q; return 1; - error: + decode_error: return 0; } #endif /* DUK_USE_BASE64_FASTPATH */ -DUK_EXTERNAL const char *duk_base64_encode(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) { const duk_uint8_t *src; duk_size_t srclen; duk_size_t dstlen; duk_uint8_t *dst; const char *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* XXX: optimize for string inputs: no need to coerce to a buffer - * which makes a copy of the input. - */ + idx = duk_require_normalize_index(thr, idx); + src = duk__prep_codec_arg(thr, idx, &srclen); + DUK_ASSERT(src != NULL); - index = duk_require_normalize_index(ctx, index); - src = duk__prep_codec_arg(ctx, index, &srclen); - /* Note: for srclen=0, src may be NULL */ - - /* Computation must not wrap; this limit works for 32-bit size_t: + /* Compute exact output length. Computation must not wrap; this + * limit works for 32-bit size_t: * >>> srclen = 3221225469 * >>> '%x' % ((srclen + 2) / 3 * 4) * 'fffffffc' @@ -13336,65 +15651,77 @@ DUK_EXTERNAL const char *duk_base64_encode(duk_context *ctx, duk_idx_t index) { if (srclen > 3221225469UL) { goto type_error; } - dstlen = (srclen + 2) / 3 * 4; - dst = (duk_uint8_t *) duk_push_fixed_buffer(ctx, dstlen); + dstlen = (srclen + 2U) / 3U * 4U; + dst = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, dstlen); duk__base64_encode_helper((const duk_uint8_t *) src, srclen, dst); - ret = duk_to_string(ctx, -1); - duk_replace(ctx, index); + ret = duk_buffer_to_string(thr, -1); /* Safe, result is ASCII. */ + duk_replace(thr, idx); return ret; type_error: - DUK_ERROR_TYPE(thr, DUK_STR_ENCODE_FAILED); - return NULL; /* never here */ + DUK_ERROR_TYPE(thr, DUK_STR_BASE64_ENCODE_FAILED); + DUK_WO_NORETURN(return NULL;); } -DUK_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) { const duk_uint8_t *src; duk_size_t srclen; duk_size_t dstlen; duk_uint8_t *dst; duk_uint8_t *dst_final; - duk_bool_t retval; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* XXX: optimize for buffer inputs: no need to coerce to a string - * which causes an unnecessary interning. + idx = duk_require_normalize_index(thr, idx); + src = duk__prep_codec_arg(thr, idx, &srclen); + DUK_ASSERT(src != NULL); + + /* Round up and add safety margin. Avoid addition before division to + * avoid possibility of wrapping. Margin includes +3 for rounding up, + * and +3 for one extra group: the decoder may emit and then backtrack + * a full group (3 bytes) from zero-sized input for technical reasons. + * Similarly, 'xx' may ecause 1+3 = bytes to be emitted and then + * backtracked. */ - - index = duk_require_normalize_index(ctx, index); - src = duk__prep_codec_arg(ctx, index, &srclen); - - /* Computation must not wrap, only srclen + 3 is at risk of - * wrapping because after that the number gets smaller. - * This limit works for 32-bit size_t: - * 0x100000000 - 3 - 1 = 4294967292 - */ - if (srclen > 4294967292UL) { - goto type_error; - } - dstlen = (srclen + 3) / 4 * 3; /* upper limit, assuming no whitespace etc */ - dst = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, dstlen); + dstlen = (srclen / 4) * 3 + 6; /* upper limit, assuming no whitespace etc */ + dst = (duk_uint8_t *) duk_push_dynamic_buffer(thr, dstlen); /* Note: for dstlen=0, dst may be NULL */ - retval = duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final); - if (!retval) { + if (!duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final)) { goto type_error; } /* XXX: convert to fixed buffer? */ - (void) duk_resize_buffer(ctx, -1, (duk_size_t) (dst_final - dst)); - duk_replace(ctx, index); + (void) duk_resize_buffer(thr, -1, (duk_size_t) (dst_final - dst)); + duk_replace(thr, idx); return; type_error: - DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED); + DUK_ERROR_TYPE(thr, DUK_STR_BASE64_DECODE_FAILED); + DUK_WO_NORETURN(return;); +} +#else /* DUK_USE_BASE64_SUPPORT */ +DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) { + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return NULL;); } -DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) { + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_BASE64_SUPPORT */ + +/* + * Hex + */ + +#if defined(DUK_USE_HEX_SUPPORT) +DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) { const duk_uint8_t *inp; duk_size_t len; duk_size_t i; @@ -13405,14 +15732,14 @@ DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t index) { duk_uint16_t *p16; #endif - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - inp = duk__prep_codec_arg(ctx, index, &len); - DUK_ASSERT(inp != NULL || len == 0); + idx = duk_require_normalize_index(thr, idx); + inp = duk__prep_codec_arg(thr, idx, &len); + DUK_ASSERT(inp != NULL); /* Fixed buffer, no zeroing because we'll fill all the data. */ - buf = (duk_uint8_t *) duk_push_buffer_raw(ctx, len * 2, DUK_BUF_FLAG_NOZERO /*flags*/); + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len * 2); DUK_ASSERT(buf != NULL); #if defined(DUK_USE_HEX_FASTPATH) @@ -13445,13 +15772,12 @@ DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t index) { * caller coerce to string if necessary? */ - ret = duk_to_string(ctx, -1); - duk_replace(ctx, index); + ret = duk_buffer_to_string(thr, -1); /* Safe, result is ASCII. */ + duk_replace(thr, idx); return ret; } -DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) { const duk_uint8_t *inp; duk_size_t len; duk_size_t i; @@ -13463,18 +15789,18 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) { duk_size_t len_safe; #endif - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - inp = duk__prep_codec_arg(ctx, index, &len); - DUK_ASSERT(inp != NULL || len == 0); + idx = duk_require_normalize_index(thr, idx); + inp = duk__prep_codec_arg(thr, idx, &len); + DUK_ASSERT(inp != NULL); if (len & 0x01) { goto type_error; } /* Fixed buffer, no zeroing because we'll fill all the data. */ - buf = (duk_uint8_t *) duk_push_buffer_raw(ctx, len / 2, DUK_BUF_FLAG_NOZERO /*flags*/); + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len / 2); DUK_ASSERT(buf != NULL); #if defined(DUK_USE_HEX_FASTPATH) @@ -13505,8 +15831,12 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) { } } for (; i < len; i += 2) { - t = (((duk_int_t) duk_hex_dectab[inp[i]]) << 4) | - ((duk_int_t) duk_hex_dectab[inp[i + 1]]); + /* First cast to duk_int_t to sign extend, second cast to + * duk_uint_t to avoid signed left shift, and final cast to + * duk_int_t result type. + */ + t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) | + ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]])); if (DUK_UNLIKELY(t < 0)) { goto type_error; } @@ -13518,8 +15848,8 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) { * at least 16 bits. If either nybble is invalid, the * resulting 't' will be < 0. */ - t = (((duk_int_t) duk_hex_dectab[inp[i]]) << 4) | - ((duk_int_t) duk_hex_dectab[inp[i + 1]]); + t = (duk_int_t) ((((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i]]) << 4U) | + ((duk_uint_t) (duk_int_t) duk_hex_dectab[inp[i + 1]])); if (DUK_UNLIKELY(t < 0)) { goto type_error; } @@ -13527,64 +15857,96 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) { } #endif /* DUK_USE_HEX_FASTPATH */ - duk_replace(ctx, index); + duk_replace(thr, idx); return; type_error: - DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED); + DUK_ERROR_TYPE(thr, DUK_STR_HEX_DECODE_FAILED); + DUK_WO_NORETURN(return;); } +#else /* DUK_USE_HEX_SUPPORT */ +DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) { + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return NULL;); +} +DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) { + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_HEX_SUPPORT */ -DUK_EXTERNAL const char *duk_json_encode(duk_context *ctx, duk_idx_t index) { -#ifdef DUK_USE_ASSERTIONS +/* + * JSON + */ + +#if defined(DUK_USE_JSON_SUPPORT) +DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) { +#if defined(DUK_USE_ASSERTIONS) duk_idx_t top_at_entry; #endif const char *ret; - DUK_ASSERT_CTX_VALID(ctx); -#ifdef DUK_USE_ASSERTIONS - top_at_entry = duk_get_top(ctx); + DUK_ASSERT_API_ENTRY(thr); +#if defined(DUK_USE_ASSERTIONS) + top_at_entry = duk_get_top(thr); #endif - index = duk_require_normalize_index(ctx, index); - duk_bi_json_stringify_helper(ctx, - index /*idx_value*/, + idx = duk_require_normalize_index(thr, idx); + duk_bi_json_stringify_helper(thr, + idx /*idx_value*/, DUK_INVALID_INDEX /*idx_replacer*/, DUK_INVALID_INDEX /*idx_space*/, 0 /*flags*/); - DUK_ASSERT(duk_is_string(ctx, -1)); - duk_replace(ctx, index); - ret = duk_get_string(ctx, index); + DUK_ASSERT(duk_is_string(thr, -1)); + duk_replace(thr, idx); + ret = duk_get_string(thr, idx); - DUK_ASSERT(duk_get_top(ctx) == top_at_entry); + DUK_ASSERT(duk_get_top(thr) == top_at_entry); return ret; } -DUK_EXTERNAL void duk_json_decode(duk_context *ctx, duk_idx_t index) { -#ifdef DUK_USE_ASSERTIONS +DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) { +#if defined(DUK_USE_ASSERTIONS) duk_idx_t top_at_entry; #endif - DUK_ASSERT_CTX_VALID(ctx); -#ifdef DUK_USE_ASSERTIONS - top_at_entry = duk_get_top(ctx); + DUK_ASSERT_API_ENTRY(thr); +#if defined(DUK_USE_ASSERTIONS) + top_at_entry = duk_get_top(thr); #endif - index = duk_require_normalize_index(ctx, index); - duk_bi_json_parse_helper(ctx, - index /*idx_value*/, + idx = duk_require_normalize_index(thr, idx); + duk_bi_json_parse_helper(thr, + idx /*idx_value*/, DUK_INVALID_INDEX /*idx_reviver*/, 0 /*flags*/); - duk_replace(ctx, index); + duk_replace(thr, idx); - DUK_ASSERT(duk_get_top(ctx) == top_at_entry); + DUK_ASSERT(duk_get_top(thr) == top_at_entry); } -#line 1 "duk_api_compile.c" +#else /* DUK_USE_JSON_SUPPORT */ +DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return NULL;); +} + +DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_JSON_SUPPORT */ /* * Compilation and evaluation */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ typedef struct duk__compile_raw_args duk__compile_raw_args; struct duk__compile_raw_args { @@ -13594,11 +15956,10 @@ struct duk__compile_raw_args { }; /* Eval is just a wrapper now. */ -DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { - duk_uint_t comp_flags; +DUK_EXTERNAL duk_int_t duk_eval_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { duk_int_t rc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* Note: strictness is *not* inherited from the current Duktape/C. * This would be confusing because the current strictness state @@ -13609,9 +15970,7 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du /* [ ... source? filename? ] (depends on flags) */ - comp_flags = flags; - comp_flags |= DUK_COMPILE_EVAL; - rc = duk_compile_raw(ctx, src_buffer, src_length, comp_flags); /* may be safe, or non-safe depending on flags */ + rc = duk_compile_raw(thr, src_buffer, src_length, flags | DUK_COMPILE_EVAL); /* may be safe, or non-safe depending on flags */ /* [ ... closure/error ] */ @@ -13620,12 +15979,12 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du goto got_rc; } - duk_push_global_object(ctx); /* explicit 'this' binding, see GH-164 */ + duk_push_global_object(thr); /* explicit 'this' binding, see GH-164 */ if (flags & DUK_COMPILE_SAFE) { - rc = duk_pcall_method(ctx, 0); + rc = duk_pcall_method(thr, 0); } else { - duk_call_method(ctx, 0); + duk_call_method(thr, 0); rc = DUK_EXEC_SUCCESS; } @@ -13633,21 +15992,20 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du got_rc: if (flags & DUK_COMPILE_NORESULT) { - duk_pop(ctx); + duk_pop(thr); } return rc; } /* Helper which can be called both directly and with duk_safe_call(). */ -DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL duk_ret_t duk__do_compile(duk_hthread *thr, void *udata) { duk__compile_raw_args *comp_args; duk_uint_t flags; - duk_small_uint_t comp_flags; - duk_hcompiledfunction *h_templ; + duk_hcompfunc *h_templ; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(udata != NULL); /* Note: strictness is not inherited from the current Duktape/C * context. Otherwise it would not be possible to compile @@ -13656,17 +16014,14 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { * for discussion. */ - /* [ ... source? filename? &comp_args ] (depends on flags) */ + /* [ ... source? filename? ] (depends on flags) */ - comp_args = (duk__compile_raw_args *) duk_require_pointer(ctx, -1); + comp_args = (duk__compile_raw_args *) udata; flags = comp_args->flags; - duk_pop(ctx); - - /* [ ... source? filename? ] */ if (flags & DUK_COMPILE_NOFILENAME) { /* Automatic filename: 'eval' or 'input'. */ - duk_push_hstring_stridx(ctx, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT); + duk_push_hstring_stridx(thr, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT); } /* [ ... source? filename ] */ @@ -13674,14 +16029,11 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { if (!comp_args->src_buffer) { duk_hstring *h_sourcecode; - h_sourcecode = duk_get_hstring(ctx, -2); + h_sourcecode = duk_get_hstring(thr, -2); if ((flags & DUK_COMPILE_NOSOURCE) || /* args incorrect */ (h_sourcecode == NULL)) { /* e.g. duk_push_string_file_raw() pushed undefined */ - /* XXX: when this error is caused by a nonexistent - * file given to duk_peval_file() or similar, the - * error message is not the best possible. - */ - DUK_ERROR_API(thr, DUK_STR_NO_SOURCECODE); + DUK_ERROR_TYPE(thr, DUK_STR_NO_SOURCECODE); + DUK_WO_NORETURN(return 0;); } DUK_ASSERT(h_sourcecode != NULL); comp_args->src_buffer = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode); @@ -13689,52 +16041,42 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx) { } DUK_ASSERT(comp_args->src_buffer != NULL); - /* XXX: unnecessary translation of flags */ - comp_flags = 0; - if (flags & DUK_COMPILE_EVAL) { - comp_flags |= DUK_JS_COMPILE_FLAG_EVAL; - } if (flags & DUK_COMPILE_FUNCTION) { - comp_flags |= DUK_JS_COMPILE_FLAG_EVAL | - DUK_JS_COMPILE_FLAG_FUNCEXPR; - } - if (flags & DUK_COMPILE_STRICT) { - comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; + flags |= DUK_COMPILE_EVAL | DUK_COMPILE_FUNCEXPR; } /* [ ... source? filename ] */ - duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, comp_flags); + duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, flags); /* [ ... source? func_template ] */ if (flags & DUK_COMPILE_NOSOURCE) { ; } else { - duk_remove(ctx, -2); + duk_remove_m2(thr); } /* [ ... func_template ] */ - h_templ = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); - DUK_ASSERT(h_templ != NULL); + h_templ = (duk_hcompfunc *) duk_known_hobject(thr, -1); duk_js_push_closure(thr, h_templ, thr->builtins[DUK_BIDX_GLOBAL_ENV], thr->builtins[DUK_BIDX_GLOBAL_ENV], 1 /*add_auto_proto*/); - duk_remove(ctx, -2); /* -> [ ... closure ] */ + duk_remove_m2(thr); /* -> [ ... closure ] */ /* [ ... closure ] */ return 1; } -DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { +DUK_EXTERNAL duk_int_t duk_compile_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) { duk__compile_raw_args comp_args_alloc; duk__compile_raw_args *comp_args = &comp_args_alloc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); if ((flags & DUK_COMPILE_STRLEN) && (src_buffer != NULL)) { /* String length is computed here to avoid multiple evaluation @@ -13746,9 +16088,8 @@ DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, comp_args->src_buffer = (const duk_uint8_t *) src_buffer; comp_args->src_length = src_length; comp_args->flags = flags; - duk_push_pointer(ctx, (void *) comp_args); - /* [ ... source? filename? &comp_args ] (depends on flags) */ + /* [ ... source? filename? ] (depends on flags) */ if (flags & DUK_COMPILE_SAFE) { duk_int_t rc; @@ -13760,73 +16101,78 @@ DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, * directly into flags. */ nargs = flags & 0x07; - DUK_ASSERT(nargs == (1 + - ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) + - ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1))); - rc = duk_safe_call(ctx, duk__do_compile, nargs, nrets); + DUK_ASSERT(nargs == ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) + + ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1)); + rc = duk_safe_call(thr, duk__do_compile, (void *) comp_args, nargs, nrets); /* [ ... closure ] */ return rc; } - (void) duk__do_compile(ctx); + (void) duk__do_compile(thr, (void *) comp_args); /* [ ... closure ] */ return DUK_EXEC_SUCCESS; } -#line 1 "duk_api_debug.c" /* * Debugging related API calls */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) { +#if defined(DUK_USE_JSON_SUPPORT) +DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) { duk_idx_t idx; duk_idx_t top; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* We don't duk_require_stack() here now, but rely on the caller having * enough space. */ - top = duk_get_top(ctx); - duk_push_array(ctx); + top = duk_get_top(thr); + duk_push_bare_array(thr); for (idx = 0; idx < top; idx++) { - duk_dup(ctx, idx); - duk_put_prop_index(ctx, -2, idx); + duk_dup(thr, idx); + duk_put_prop_index(thr, -2, (duk_uarridx_t) idx); } /* XXX: conversion errors should not propagate outwards. * Perhaps values need to be coerced individually? */ - duk_bi_json_stringify_helper(ctx, - duk_get_top_index(ctx), /*idx_value*/ + duk_bi_json_stringify_helper(thr, + duk_get_top_index(thr), /*idx_value*/ DUK_INVALID_INDEX, /*idx_replacer*/ DUK_INVALID_INDEX, /*idx_space*/ DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_ASCII_ONLY | DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/); - duk_push_sprintf(ctx, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(ctx, -1)); - duk_replace(ctx, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */ - duk_pop(ctx); - DUK_ASSERT(duk_is_string(ctx, -1)); + duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1)); + duk_replace(thr, -3); /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */ + duk_pop(thr); + DUK_ASSERT(duk_is_string(thr, -1)); } +#else /* DUK_USE_JSON_SUPPORT */ +DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_JSON_SUPPORT */ #if defined(DUK_USE_DEBUGGER_SUPPORT) -DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx, - duk_debug_read_function read_cb, - duk_debug_write_function write_cb, - duk_debug_peek_function peek_cb, - duk_debug_read_flush_function read_flush_cb, - duk_debug_write_flush_function write_flush_cb, - duk_debug_request_function request_cb, - duk_debug_detached_function detached_cb, - void *udata) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr, + duk_debug_read_function read_cb, + duk_debug_write_function write_cb, + duk_debug_peek_function peek_cb, + duk_debug_read_flush_function read_flush_cb, + duk_debug_write_flush_function write_flush_cb, + duk_debug_request_function request_cb, + duk_debug_detached_function detached_cb, + void *udata) { duk_heap *heap; const char *str; duk_size_t len; @@ -13837,7 +16183,7 @@ DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx, DUK_D(DUK_DPRINT("application called duk_debugger_attach()")); - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(read_cb != NULL); DUK_ASSERT(write_cb != NULL); /* Other callbacks are optional. */ @@ -13855,59 +16201,51 @@ DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx, /* Start in paused state. */ heap->dbg_processing = 0; - heap->dbg_paused = 1; - heap->dbg_state_dirty = 1; + heap->dbg_state_dirty = 0; heap->dbg_force_restart = 0; - heap->dbg_step_type = 0; - heap->dbg_step_thread = NULL; - heap->dbg_step_csindex = 0; - heap->dbg_step_startline = 0; + heap->dbg_pause_flags = 0; + heap->dbg_pause_act = NULL; + heap->dbg_pause_startline = 0; heap->dbg_exec_counter = 0; heap->dbg_last_counter = 0; heap->dbg_last_time = 0.0; + duk_debug_set_paused(heap); /* XXX: overlap with fields above */ /* Send version identification and flush right afterwards. Note that * we must write raw, unframed bytes here. */ - duk_push_sprintf(ctx, "%ld %ld %s %s\n", + duk_push_sprintf(thr, "%ld %ld %s %s\n", (long) DUK_DEBUG_PROTOCOL_VERSION, (long) DUK_VERSION, (const char *) DUK_GIT_DESCRIBE, (const char *) DUK_USE_TARGET_INFO); - str = duk_get_lstring(ctx, -1, &len); + str = duk_get_lstring(thr, -1, &len); DUK_ASSERT(str != NULL); duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len); duk_debug_write_flush(thr); - duk_pop(ctx); + duk_pop(thr); } -DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) { - duk_hthread *thr; - +DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) { DUK_D(DUK_DPRINT("application called duk_debugger_detach()")); - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); /* Can be called multiple times with no harm. */ duk_debug_do_detach(thr->heap); } -DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) { duk_bool_t processed_messages; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); - if (!DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (!duk_debug_is_attached(thr->heap)) { return; } - if (thr->callstack_top > 0 || thr->heap->dbg_processing) { + if (thr->callstack_curr != NULL || thr->heap->dbg_processing) { /* Calling duk_debugger_cooperate() while Duktape is being * called into is not supported. This is not a 100% check * but prevents any damage in most cases. @@ -13919,28 +16257,25 @@ DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { DUK_UNREF(processed_messages); } -DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) { - duk_hthread *thr; +DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) { duk_idx_t top; duk_idx_t idx; duk_bool_t ret = 0; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues)); - top = duk_get_top(ctx); + top = duk_get_top(thr); if (top < nvalues) { - DUK_ERROR_API(thr, "not enough stack values for notify"); - return ret; /* unreachable */ + DUK_ERROR_RANGE(thr, "not enough stack values for notify"); + DUK_WO_NORETURN(return 0;); } - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (duk_debug_is_attached(thr->heap)) { duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY); for (idx = top - nvalues; idx < top; idx++) { - duk_tval *tv = DUK_GET_TVAL_POSIDX(ctx, idx); + duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, idx); duk_debug_write_tval(thr, tv); } duk_debug_write_eom(thr); @@ -13950,49 +16285,49 @@ DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) * a transport error was not indicated by the transport write * callback. This is not a 100% guarantee of course. */ - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (duk_debug_is_attached(thr->heap)) { ret = 1; } } - duk_pop_n(ctx, nvalues); + duk_pop_n(thr, nvalues); return ret; } -DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) { - duk_hthread *thr; - - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_ASSERT(thr != NULL); +DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); DUK_D(DUK_DPRINT("application called duk_debugger_pause()")); /* Treat like a debugger statement: ignore when not attached. */ - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { - DUK_HEAP_SET_PAUSED(thr->heap); + if (duk_debug_is_attached(thr->heap)) { + if (duk_debug_is_paused(thr->heap)) { + DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring")); + } else { + duk_debug_set_paused(thr->heap); - /* Pause on the next opcode executed. This is always safe to do even - * inside the debugger message loop: the interrupt counter will be reset - * to its proper value when the message loop exits. - */ - thr->interrupt_init = 1; - thr->interrupt_counter = 0; + /* Pause on the next opcode executed. This is always safe to do even + * inside the debugger message loop: the interrupt counter will be reset + * to its proper value when the message loop exits. + */ + thr->interrupt_init = 1; + thr->interrupt_counter = 0; + } } } #else /* DUK_USE_DEBUGGER_SUPPORT */ -DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx, - duk_debug_read_function read_cb, - duk_debug_write_function write_cb, - duk_debug_peek_function peek_cb, - duk_debug_read_flush_function read_flush_cb, - duk_debug_write_flush_function write_flush_cb, - duk_debug_request_function request_cb, - duk_debug_detached_function detached_cb, - void *udata) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr, + duk_debug_read_function read_cb, + duk_debug_write_function write_cb, + duk_debug_peek_function peek_cb, + duk_debug_read_flush_function read_flush_cb, + duk_debug_write_flush_function write_flush_cb, + duk_debug_request_function request_cb, + duk_debug_detached_function detached_cb, + void *udata) { + DUK_ASSERT_API_ENTRY(thr); DUK_UNREF(read_cb); DUK_UNREF(write_cb); DUK_UNREF(peek_cb); @@ -14001,58 +16336,68 @@ DUK_EXTERNAL void duk_debugger_attach_custom(duk_context *ctx, DUK_UNREF(request_cb); DUK_UNREF(detached_cb); DUK_UNREF(udata); - DUK_ERROR_API((duk_hthread *) ctx, "no debugger support"); + DUK_ERROR_TYPE(thr, "no debugger support"); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - DUK_ERROR_API((duk_hthread *) ctx, "no debugger support"); +DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_TYPE(thr, "no debugger support"); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { +DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) { /* nop */ - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(ctx); + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); } -DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) { +DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) { duk_idx_t top; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - top = duk_get_top(ctx); + top = duk_get_top(thr); if (top < nvalues) { - DUK_ERROR_API((duk_hthread *) ctx, "not enough stack values for notify"); - return 0; /* unreachable */ + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return 0;); } /* No debugger support, just pop values. */ - duk_pop_n(ctx, nvalues); + duk_pop_n(thr, nvalues); return 0; } -DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) { +DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) { /* Treat like debugger statement: nop */ - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(ctx); + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); } #endif /* DUK_USE_DEBUGGER_SUPPORT */ -#line 1 "duk_api_heap.c" /* * Heap creation and destruction */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL -duk_context *duk_create_heap(duk_alloc_function alloc_func, - duk_realloc_function realloc_func, - duk_free_function free_func, - void *heap_udata, - duk_fatal_function fatal_handler) { +typedef struct duk_internal_thread_state duk_internal_thread_state; + +struct duk_internal_thread_state { + duk_ljstate lj; + duk_bool_t creating_error; + duk_hthread *curr_thread; + duk_uint8_t thread_state; + duk_int_t call_recursion_depth; +}; + +DUK_EXTERNAL duk_hthread *duk_create_heap(duk_alloc_function alloc_func, + duk_realloc_function realloc_func, + duk_free_function free_func, + void *heap_udata, + duk_fatal_function fatal_handler) { duk_heap *heap = NULL; - duk_context *ctx; + duk_hthread *thr; /* Assume that either all memory funcs are NULL or non-NULL, mixed * cases will now be unsafe. @@ -14091,36 +16436,108 @@ duk_context *duk_create_heap(duk_alloc_function alloc_func, if (!heap) { return NULL; } - ctx = (duk_context *) heap->heap_thread; - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(((duk_hthread *) ctx)->heap != NULL); - return ctx; + thr = heap->heap_thread; + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + return thr; } -DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) { duk_heap *heap; - if (!ctx) { + if (!thr) { return; } + DUK_ASSERT_API_ENTRY(thr); heap = thr->heap; DUK_ASSERT(heap != NULL); duk_heap_free(heap); } +DUK_EXTERNAL void duk_suspend(duk_hthread *thr, duk_thread_state *state) { + duk_internal_thread_state *snapshot = (duk_internal_thread_state *) (void *) state; + duk_heap *heap; + duk_ljstate *lj; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(state != NULL); /* unvalidated */ + + /* Currently not supported when called from within a finalizer. + * If that is done, the finalizer will remain running indefinitely, + * preventing other finalizers from executing. The assert is a bit + * wider, checking that it would be OK to run pending finalizers. + */ + DUK_ASSERT(thr->heap->pf_prevent_count == 0); + + /* Currently not supported to duk_suspend() from an errCreate() + * call. + */ + DUK_ASSERT(thr->heap->creating_error == 0); + + heap = thr->heap; + lj = &heap->lj; + + duk_push_tval(thr, &lj->value1); + duk_push_tval(thr, &lj->value2); + + /* XXX: creating_error == 0 is asserted above, so no need to store. */ + duk_memcpy((void *) &snapshot->lj, (const void *) lj, sizeof(duk_ljstate)); + snapshot->creating_error = heap->creating_error; + snapshot->curr_thread = heap->curr_thread; + snapshot->thread_state = thr->state; + snapshot->call_recursion_depth = heap->call_recursion_depth; + + lj->jmpbuf_ptr = NULL; + lj->type = DUK_LJ_TYPE_UNKNOWN; + DUK_TVAL_SET_UNDEFINED(&lj->value1); + DUK_TVAL_SET_UNDEFINED(&lj->value2); + heap->creating_error = 0; + heap->curr_thread = NULL; + heap->call_recursion_depth = 0; + + thr->state = DUK_HTHREAD_STATE_INACTIVE; +} + +DUK_EXTERNAL void duk_resume(duk_hthread *thr, const duk_thread_state *state) { + const duk_internal_thread_state *snapshot = (const duk_internal_thread_state *) (const void *) state; + duk_heap *heap; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(state != NULL); /* unvalidated */ + + /* Shouldn't be necessary if duk_suspend() is called before + * duk_resume(), but assert in case API sequence is incorrect. + */ + DUK_ASSERT(thr->heap->pf_prevent_count == 0); + DUK_ASSERT(thr->heap->creating_error == 0); + + thr->state = snapshot->thread_state; + + heap = thr->heap; + + duk_memcpy((void *) &heap->lj, (const void *) &snapshot->lj, sizeof(duk_ljstate)); + heap->creating_error = snapshot->creating_error; + heap->curr_thread = snapshot->curr_thread; + heap->call_recursion_depth = snapshot->call_recursion_depth; + + duk_pop_2(thr); +} + /* XXX: better place for this */ -DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_set_global_object(duk_hthread *thr) { duk_hobject *h_glob; duk_hobject *h_prev_glob; - duk_hobject *h_env; + duk_hobjenv *h_env; duk_hobject *h_prev_env; - DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(ctx, -1))); + DUK_ASSERT_API_ENTRY(thr); - h_glob = duk_require_hobject(ctx, -1); + DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1))); + + h_glob = duk_require_hobject(thr, -1); DUK_ASSERT(h_glob != NULL); /* @@ -14142,141 +16559,316 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { * same (initial) built-ins. */ - (void) duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), - -1); /* no prototype, updated below */ - - duk_dup(ctx, -2); - duk_dup(ctx, -3); - - /* [ ... new_glob new_env new_glob new_glob ] */ - - duk_xdef_prop_stridx(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); - duk_xdef_prop_stridx(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); - - /* [ ... new_glob new_env ] */ - - h_env = duk_get_hobject(ctx, -1); + h_env = duk_hobjenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); DUK_ASSERT(h_env != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_env) == NULL); + + DUK_ASSERT(h_env->target == NULL); + DUK_ASSERT(h_glob != NULL); + h_env->target = h_glob; + DUK_HOBJECT_INCREF(thr, h_glob); + DUK_ASSERT(h_env->has_this == 0); + + /* [ ... new_glob ] */ h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_env; - DUK_HOBJECT_INCREF(thr, h_env); + thr->builtins[DUK_BIDX_GLOBAL_ENV] = (duk_hobject *) h_env; + DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_env); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ DUK_UNREF(h_env); /* without refcounts */ DUK_UNREF(h_prev_env); - /* [ ... new_glob new_env ] */ + /* [ ... new_glob ] */ - duk_pop_2(ctx); + duk_pop(thr); /* [ ... ] */ } -#line 1 "duk_api_logging.c" /* - * Logging - * - * Current logging primitive is a sprintf-style log which is convenient - * for most C code. Another useful primitive would be to log N arguments - * from value stack (like the Ecmascript binding does). + * Inspection */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL void duk_log_va(duk_context *ctx, duk_int_t level, const char *fmt, va_list ap) { - /* stridx_logfunc[] must be static to allow initializer with old compilers like BCC */ - static const duk_uint16_t stridx_logfunc[6] = { - DUK_STRIDX_LC_TRACE, DUK_STRIDX_LC_DEBUG, DUK_STRIDX_LC_INFO, - DUK_STRIDX_LC_WARN, DUK_STRIDX_LC_ERROR, DUK_STRIDX_LC_FATAL - }; +/* For footprint efficient multiple value setting: arrays are much better than + * varargs, format string with parsing is often better than string pointer arrays. + */ +DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) { + duk_int_t val; + const char *p; + const char *p_curr; + duk_size_t len; - DUK_ASSERT_CTX_VALID(ctx); + for (p = fmt;;) { + len = DUK_STRLEN(p); + p_curr = p; + p += len + 1; + if (len == 0) { + /* Double NUL (= empty key) terminates. */ + break; + } + val = *vals++; + if (val >= 0) { + /* Negative values are markers to skip key. */ + duk_push_string(thr, p_curr); + duk_push_int(thr, val); + duk_put_prop(thr, -3); + } + } +} - if (level < 0) { - level = 0; - } else if (level > (int) (sizeof(stridx_logfunc) / sizeof(duk_uint16_t)) - 1) { - level = (int) (sizeof(stridx_logfunc) / sizeof(duk_uint16_t)) - 1; +/* Raw helper to extract internal information / statistics about a value. + * The return value is an object with properties that are version specific. + * The properties must not expose anything that would lead to security + * issues (e.g. exposing compiled function 'data' buffer might be an issue). + * Currently only counts and sizes and such are given so there shouldn't + * be security implications. + */ + +#define DUK__IDX_TYPE 0 +#define DUK__IDX_ITAG 1 +#define DUK__IDX_REFC 2 +#define DUK__IDX_HBYTES 3 +#define DUK__IDX_CLASS 4 +#define DUK__IDX_PBYTES 5 +#define DUK__IDX_ESIZE 6 +#define DUK__IDX_ENEXT 7 +#define DUK__IDX_ASIZE 8 +#define DUK__IDX_HSIZE 9 +#define DUK__IDX_BCBYTES 10 +#define DUK__IDX_DBYTES 11 +#define DUK__IDX_TSTATE 12 +#define DUK__IDX_VARIANT 13 + +DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + duk_heaphdr *h; + /* The temporary values should be in an array rather than individual + * variables which (in practice) ensures that the compiler won't map + * them to registers and emit a lot of unnecessary shuffling code. + */ + duk_int_t vals[14]; + + DUK_ASSERT_API_ENTRY(thr); + + /* Assume two's complement and set everything to -1. */ + duk_memset((void *) &vals, (int) 0xff, sizeof(vals)); + DUK_ASSERT(vals[DUK__IDX_TYPE] == -1); /* spot check one */ + + tv = duk_get_tval_or_unused(thr, idx); + h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL); + + vals[DUK__IDX_TYPE] = duk_get_type_tval(tv); + vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv); + + duk_push_bare_object(thr); /* Invalidates 'tv'. */ + tv = NULL; + + if (h == NULL) { + goto finish; + } + duk_push_pointer(thr, (void *) h); + duk_put_prop_literal(thr, -2, "hptr"); + +#if 0 + /* Covers a lot of information, e.g. buffer and string variants. */ + duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h)); + duk_put_prop_literal(thr, -2, "hflags"); +#endif + +#if defined(DUK_USE_REFERENCE_COUNTING) + vals[DUK__IDX_REFC] = (duk_int_t) DUK_HEAPHDR_GET_REFCOUNT(h); +#endif + vals[DUK__IDX_VARIANT] = 0; + + /* Heaphdr size and additional allocation size, followed by + * type specific stuff (with varying value count). + */ + switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { + case DUK_HTYPE_STRING: { + duk_hstring *h_str = (duk_hstring *) h; + vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1); +#if defined(DUK_USE_HSTRING_EXTDATA) + if (DUK_HSTRING_HAS_EXTDATA(h_str)) { + vals[DUK__IDX_VARIANT] = 1; + } +#endif + break; + } + case DUK_HTYPE_OBJECT: { + duk_hobject *h_obj = (duk_hobject *) h; + + /* XXX: variants here are maybe pointless; class is enough? */ + if (DUK_HOBJECT_IS_ARRAY(h_obj)) { + vals[DUK__IDX_HBYTES] = sizeof(duk_harray); + } else if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { + vals[DUK__IDX_HBYTES] = sizeof(duk_hcompfunc); + } else if (DUK_HOBJECT_IS_NATFUNC(h_obj)) { + vals[DUK__IDX_HBYTES] = sizeof(duk_hnatfunc); + } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { + vals[DUK__IDX_HBYTES] = sizeof(duk_hthread); + vals[DUK__IDX_TSTATE] = ((duk_hthread *) h_obj)->state; +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) + } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { + vals[DUK__IDX_HBYTES] = sizeof(duk_hbufobj); + /* XXX: some size information */ +#endif + } else { + vals[DUK__IDX_HBYTES] = (duk_small_uint_t) sizeof(duk_hobject); + } + + vals[DUK__IDX_CLASS] = (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h_obj); + vals[DUK__IDX_PBYTES] = (duk_int_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj); + vals[DUK__IDX_ESIZE] = (duk_int_t) DUK_HOBJECT_GET_ESIZE(h_obj); + vals[DUK__IDX_ENEXT] = (duk_int_t) DUK_HOBJECT_GET_ENEXT(h_obj); + vals[DUK__IDX_ASIZE] = (duk_int_t) DUK_HOBJECT_GET_ASIZE(h_obj); + vals[DUK__IDX_HSIZE] = (duk_int_t) DUK_HOBJECT_GET_HSIZE(h_obj); + + /* Note: e_next indicates the number of gc-reachable entries + * in the entry part, and also indicates the index where the + * next new property would be inserted. It does *not* indicate + * the number of non-NULL keys present in the object. That + * value could be counted separately but requires a pass through + * the key list. + */ + + if (DUK_HOBJECT_IS_COMPFUNC(h_obj)) { + duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, (duk_hcompfunc *) h_obj); + vals[DUK__IDX_BCBYTES] = (duk_int_t) (h_data ? DUK_HBUFFER_GET_SIZE(h_data) : 0); + } + break; + } + case DUK_HTYPE_BUFFER: { + duk_hbuffer *h_buf = (duk_hbuffer *) h; + + if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { + if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { + vals[DUK__IDX_VARIANT] = 2; /* buffer variant 2: external */ + vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_external)); + } else { + /* When alloc_size == 0 the second allocation may not + * actually exist. + */ + vals[DUK__IDX_VARIANT] = 1; /* buffer variant 1: dynamic */ + vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic)); + } + vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf)); + } else { + DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0); /* buffer variant 0: fixed */ + vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf)); + } + break; + } } - duk_push_hobject_bidx(ctx, DUK_BIDX_LOGGER_CONSTRUCTOR); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_CLOG); - duk_get_prop_stridx(ctx, -1, stridx_logfunc[level]); - duk_dup(ctx, -2); - - /* [ ... Logger clog logfunc clog ] */ - - duk_push_vsprintf(ctx, fmt, ap); - - /* [ ... Logger clog logfunc clog(=this) msg ] */ - - duk_call_method(ctx, 1 /*nargs*/); - - /* [ ... Logger clog res ] */ - - duk_pop_3(ctx); + finish: + duk__inspect_multiple_uint(thr, + "type" "\x00" "itag" "\x00" "refc" "\x00" "hbytes" "\x00" "class" "\x00" + "pbytes" "\x00" "esize" "\x00" "enext" "\x00" "asize" "\x00" "hsize" "\x00" + "bcbytes" "\x00" "dbytes" "\x00" "tstate" "\x00" "variant" "\x00" "\x00", + (duk_int_t *) &vals); } -DUK_EXTERNAL void duk_log(duk_context *ctx, duk_int_t level, const char *fmt, ...) { - va_list ap; +DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) { + duk_activation *act; + duk_uint_fast32_t pc; + duk_uint_fast32_t line; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - va_start(ap, fmt); - duk_log_va(ctx, level, fmt, ap); - va_end(ap); + /* -1 = top callstack entry + * -2 = caller of level -1 + * etc + */ + act = duk_hthread_get_activation_for_level(thr, level); + if (act == NULL) { + duk_push_undefined(thr); + return; + } + duk_push_bare_object(thr); + + /* Relevant PC is just before current one because PC is + * post-incremented. This should match what error augment + * code does. + */ + pc = duk_hthread_get_act_prev_pc(thr, act); + + duk_push_tval(thr, &act->tv_func); + + duk_push_uint(thr, (duk_uint_t) pc); + duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC); + +#if defined(DUK_USE_PC2LINE) + line = duk_hobject_pc2line_query(thr, -1, pc); +#else + line = 0; +#endif + duk_push_uint(thr, (duk_uint_t) line); + duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER); + + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION); + /* Providing access to e.g. act->lex_env would be dangerous: these + * internal structures must never be accessible to the application. + * Duktape relies on them having consistent data, and this consistency + * is only asserted for, not checked for. + */ } -#line 1 "duk_api_memory.c" + +/* automatic undefs */ +#undef DUK__IDX_ASIZE +#undef DUK__IDX_BCBYTES +#undef DUK__IDX_CLASS +#undef DUK__IDX_DBYTES +#undef DUK__IDX_ENEXT +#undef DUK__IDX_ESIZE +#undef DUK__IDX_HBYTES +#undef DUK__IDX_HSIZE +#undef DUK__IDX_ITAG +#undef DUK__IDX_PBYTES +#undef DUK__IDX_REFC +#undef DUK__IDX_TSTATE +#undef DUK__IDX_TYPE +#undef DUK__IDX_VARIANT /* * Memory calls. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL void *duk_alloc_raw(duk_context *ctx, duk_size_t size) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void *duk_alloc_raw(duk_hthread *thr, duk_size_t size) { + DUK_ASSERT_API_ENTRY(thr); return DUK_ALLOC_RAW(thr->heap, size); } -DUK_EXTERNAL void duk_free_raw(duk_context *ctx, void *ptr) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_free_raw(duk_hthread *thr, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); DUK_FREE_RAW(thr->heap, ptr); } -DUK_EXTERNAL void *duk_realloc_raw(duk_context *ctx, void *ptr, duk_size_t size) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void *duk_realloc_raw(duk_hthread *thr, void *ptr, duk_size_t size) { + DUK_ASSERT_API_ENTRY(thr); return DUK_REALLOC_RAW(thr->heap, ptr, size); } -DUK_EXTERNAL void *duk_alloc(duk_context *ctx, duk_size_t size) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void *duk_alloc(duk_hthread *thr, duk_size_t size) { + DUK_ASSERT_API_ENTRY(thr); return DUK_ALLOC(thr->heap, size); } -DUK_EXTERNAL void duk_free(duk_context *ctx, void *ptr) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_free(duk_hthread *thr, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); - - DUK_FREE(thr->heap, ptr); + DUK_FREE_CHECKED(thr, ptr); } -DUK_EXTERNAL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void *duk_realloc(duk_hthread *thr, void *ptr, duk_size_t size) { + DUK_ASSERT_API_ENTRY(thr); /* * Note: since this is an exposed API call, there should be @@ -14291,11 +16883,10 @@ DUK_EXTERNAL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size) { return DUK_REALLOC(thr->heap, ptr, size); } -DUK_EXTERNAL void duk_get_memory_functions(duk_context *ctx, duk_memory_functions *out_funcs) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_get_memory_functions(duk_hthread *thr, duk_memory_functions *out_funcs) { duk_heap *heap; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(out_funcs != NULL); DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); @@ -14307,371 +16898,550 @@ DUK_EXTERNAL void duk_get_memory_functions(duk_context *ctx, duk_memory_function out_funcs->udata = heap->heap_udata; } -DUK_EXTERNAL void duk_gc(duk_context *ctx, duk_uint_t flags) { -#ifdef DUK_USE_MARK_AND_SWEEP - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_gc(duk_hthread *thr, duk_uint_t flags) { duk_heap *heap; + duk_small_uint_t ms_flags; - DUK_UNREF(flags); - - /* NULL accepted */ - if (!ctx) { - return; - } - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); heap = thr->heap; DUK_ASSERT(heap != NULL); DUK_D(DUK_DPRINT("mark-and-sweep requested by application")); - duk_heap_mark_and_sweep(heap, 0); -#else - DUK_D(DUK_DPRINT("mark-and-sweep requested by application but mark-and-sweep not enabled, ignoring")); - DUK_UNREF(ctx); - DUK_UNREF(flags); -#endif + DUK_ASSERT(DUK_GC_COMPACT == DUK_MS_FLAG_EMERGENCY); /* Compact flag is 1:1 with emergency flag which forces compaction. */ + ms_flags = (duk_small_uint_t) flags; + duk_heap_mark_and_sweep(heap, ms_flags); } -#line 1 "duk_api_object.c" /* * Object handling: property access and other support functions. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* * Property handling * * The API exposes only the most common property handling functions. - * The caller can invoke Ecmascript built-ins for full control (e.g. + * The caller can invoke ECMAScript built-ins for full control (e.g. * defineProperty, getOwnPropertyDescriptor). */ -DUK_EXTERNAL duk_bool_t duk_get_prop(duk_context *ctx, duk_idx_t obj_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_get_prop(duk_hthread *thr, duk_idx_t obj_idx) { duk_tval *tv_obj; duk_tval *tv_key; duk_bool_t rc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* Note: copying tv_obj and tv_key to locals to shield against a valstack * resize is not necessary for a property get right now. */ - tv_obj = duk_require_tval(ctx, obj_index); - tv_key = duk_require_tval(ctx, -1); + tv_obj = duk_require_tval(thr, obj_idx); + tv_key = duk_require_tval(thr, -1); rc = duk_hobject_getprop(thr, tv_obj, tv_key); DUK_ASSERT(rc == 0 || rc == 1); /* a value is left on stack regardless of rc */ - duk_remove(ctx, -2); /* remove key */ + duk_remove_m2(thr); /* remove key */ + DUK_ASSERT(duk_is_undefined(thr, -1) || rc == 1); return rc; /* 1 if property found, 0 otherwise */ } -DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_string(ctx, key); - return duk_get_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_string(thr, key); + return duk_get_prop(thr, obj_idx); } -DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_get_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_uarridx(ctx, arr_index); - return duk_get_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_lstring(thr, key, key_len); + return duk_get_prop(thr, obj_idx); } -DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { - duk_hthread *thr = (duk_hthread *) ctx; +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_get_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); + DUK_ASSERT(key[key_len] == (char) 0); - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); - DUK_UNREF(thr); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_literal_raw(thr, key, key_len); + return duk_get_prop(thr, obj_idx); +} +#endif - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_get_prop(ctx, obj_index); +DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_get_prop(thr, obj_idx); } -DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_bool_t *out_has_prop) { +DUK_EXTERNAL duk_bool_t duk_get_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ + return duk_get_prop(thr, obj_idx); +} + +DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); + return duk_get_prop(thr, obj_idx); +} + +DUK_INTERNAL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + return duk_get_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), + (duk_small_uint_t) (packed_args & 0xffffUL)); +} + +DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop) { duk_bool_t rc; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); - rc = duk_get_prop_stridx(ctx, obj_index, stridx); + rc = duk_get_prop_stridx(thr, obj_idx, stridx); if (out_has_prop) { *out_has_prop = rc; } - rc = duk_to_boolean(ctx, -1); - DUK_ASSERT(rc == 0 || rc == 1); - duk_pop(ctx); - return rc; + return duk_to_boolean_top_pop(thr); } -DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_index) { - duk_hthread *thr = (duk_hthread *) ctx; +/* This get variant is for internal use, it differs from standard + * duk_get_prop() in that: + * - Object argument must be an object (primitive values not supported). + * - Key argument must be a string (no coercion). + * - Only own properties are checked (no inheritance). Only "entry part" + * properties are checked (not array index properties). + * - Property must be a plain data property, not a getter. + * - Proxy traps are not triggered. + */ +DUK_INTERNAL duk_bool_t duk_xget_owndataprop(duk_hthread *thr, duk_idx_t obj_idx) { + duk_hobject *h_obj; + duk_hstring *h_key; + duk_tval *tv_val; + + DUK_ASSERT_API_ENTRY(thr); + + /* Note: copying tv_obj and tv_key to locals to shield against a valstack + * resize is not necessary for a property get right now. + */ + + h_obj = duk_get_hobject(thr, obj_idx); + if (h_obj == NULL) { + return 0; + } + h_key = duk_require_hstring(thr, -1); + + tv_val = duk_hobject_find_entry_tval_ptr(thr->heap, h_obj, h_key); + if (tv_val == NULL) { + return 0; + } + + duk_push_tval(thr, tv_val); + duk_remove_m2(thr); /* remove key */ + + return 1; +} + +DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); + return duk_xget_owndataprop(thr, obj_idx); +} + +DUK_INTERNAL duk_bool_t duk_xget_owndataprop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + return duk_xget_owndataprop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), + (duk_small_uint_t) (packed_args & 0xffffUL)); +} + +DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) { duk_tval *tv_obj; duk_tval *tv_key; duk_tval *tv_val; - duk_small_int_t throw_flag; + duk_bool_t throw_flag; duk_bool_t rc; - DUK_ASSERT_CTX_VALID(ctx); - /* Note: copying tv_obj and tv_key to locals to shield against a valstack * resize is not necessary for a property put right now (putprop protects * against it internally). */ - tv_obj = duk_require_tval(ctx, obj_index); - tv_key = duk_require_tval(ctx, -2); - tv_val = duk_require_tval(ctx, -1); - throw_flag = duk_is_strict_call(ctx); + /* Key and value indices are either (-2, -1) or (-1, -2). Given idx_key, + * idx_val is always (idx_key ^ 0x01). + */ + DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) || + (idx_key == -1 && (idx_key ^ 1) == -2)); + /* XXX: Direct access; faster validation. */ + tv_obj = duk_require_tval(thr, obj_idx); + tv_key = duk_require_tval(thr, idx_key); + tv_val = duk_require_tval(thr, idx_key ^ 1); + throw_flag = duk_is_strict_call(thr); rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag); DUK_ASSERT(rc == 0 || rc == 1); - duk_pop_2(ctx); /* remove key and value */ + duk_pop_2(thr); /* remove key and value */ return rc; /* 1 if property found, 0 otherwise */ } -DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_put_prop(duk_hthread *thr, duk_idx_t obj_idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__put_prop_shared(thr, obj_idx, -2); +} + +DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_string(ctx, key); - duk_swap_top(ctx, -2); /* [val key] -> [key val] */ - return duk_put_prop(ctx, obj_index); + /* Careful here and with other duk_put_prop_xxx() helpers: the + * target object and the property value may be in the same value + * stack slot (unusual, but still conceptually clear). + */ + obj_idx = duk_normalize_index(thr, obj_idx); + (void) duk_push_string(thr, key); + return duk__put_prop_shared(thr, obj_idx, -1); } -DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_put_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_uarridx(ctx, arr_index); - duk_swap_top(ctx, -2); /* [val key] -> [key val] */ - return duk_put_prop(ctx, obj_index); + obj_idx = duk_normalize_index(thr, obj_idx); + (void) duk_push_lstring(thr, key, key_len); + return duk__put_prop_shared(thr, obj_idx, -1); } -DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { - duk_hthread *thr = (duk_hthread *) ctx; +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_put_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); + DUK_ASSERT(key[key_len] == (char) 0); - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); - DUK_UNREF(thr); + obj_idx = duk_normalize_index(thr, obj_idx); + (void) duk_push_literal_raw(thr, key, key_len); + return duk__put_prop_shared(thr, obj_idx, -1); +} +#endif - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); - duk_swap_top(ctx, -2); /* [val key] -> [key val] */ - return duk_put_prop(ctx, obj_index); +DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk__put_prop_shared(thr, obj_idx, -1); } -DUK_EXTERNAL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_put_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ + return duk__put_prop_shared(thr, obj_idx, -1); +} + + +DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); + return duk__put_prop_shared(thr, obj_idx, -1); +} + +DUK_INTERNAL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + return duk_put_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), + (duk_small_uint_t) (packed_args & 0xffffUL)); +} + +DUK_EXTERNAL duk_bool_t duk_del_prop(duk_hthread *thr, duk_idx_t obj_idx) { duk_tval *tv_obj; duk_tval *tv_key; - duk_small_int_t throw_flag; + duk_bool_t throw_flag; duk_bool_t rc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* Note: copying tv_obj and tv_key to locals to shield against a valstack * resize is not necessary for a property delete right now. */ - tv_obj = duk_require_tval(ctx, obj_index); - tv_key = duk_require_tval(ctx, -1); - throw_flag = duk_is_strict_call(ctx); + tv_obj = duk_require_tval(thr, obj_idx); + tv_key = duk_require_tval(thr, -1); + throw_flag = duk_is_strict_call(thr); rc = duk_hobject_delprop(thr, tv_obj, tv_key, throw_flag); DUK_ASSERT(rc == 0 || rc == 1); - duk_pop(ctx); /* remove key */ + duk_pop(thr); /* remove key */ return rc; } -DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_string(ctx, key); - return duk_del_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_string(thr, key); + return duk_del_prop(thr, obj_idx); } -DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_del_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_uarridx(ctx, arr_index); - return duk_del_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_lstring(thr, key, key_len); + return duk_del_prop(thr, obj_idx); } -DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { - duk_hthread *thr = (duk_hthread *) ctx; +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_del_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); + DUK_ASSERT(key[key_len] == (char) 0); - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); - DUK_UNREF(thr); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_literal_raw(thr, key, key_len); + return duk_del_prop(thr, obj_idx); +} +#endif - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_del_prop(ctx, obj_index); +DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_del_prop(thr, obj_idx); } -DUK_EXTERNAL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_del_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ + return duk_del_prop(thr, obj_idx); +} + +DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); + return duk_del_prop(thr, obj_idx); +} + +#if 0 +DUK_INTERNAL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + return duk_del_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), + (duk_small_uint_t) (packed_args & 0xffffUL)); +} +#endif + +DUK_EXTERNAL duk_bool_t duk_has_prop(duk_hthread *thr, duk_idx_t obj_idx) { duk_tval *tv_obj; duk_tval *tv_key; duk_bool_t rc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* Note: copying tv_obj and tv_key to locals to shield against a valstack * resize is not necessary for a property existence check right now. */ - tv_obj = duk_require_tval(ctx, obj_index); - tv_key = duk_require_tval(ctx, -1); + tv_obj = duk_require_tval(thr, obj_idx); + tv_key = duk_require_tval(thr, -1); rc = duk_hobject_hasprop(thr, tv_obj, tv_key); DUK_ASSERT(rc == 0 || rc == 1); - duk_pop(ctx); /* remove key */ + duk_pop(thr); /* remove key */ return rc; /* 1 if property found, 0 otherwise */ } -DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_string(ctx, key); - return duk_has_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_string(thr, key); + return duk_has_prop(thr, obj_idx); } -DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_has_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_uarridx(ctx, arr_index); - return duk_has_prop(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_lstring(thr, key, key_len); + return duk_has_prop(thr, obj_idx); } -DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { - duk_hthread *thr = (duk_hthread *) ctx; +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_has_prop_literal_raw(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(key != NULL); + DUK_ASSERT(key[key_len] == (char) 0); - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); - DUK_UNREF(thr); + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_literal_raw(thr, key, key_len); + return duk_has_prop(thr, obj_idx); +} +#endif - obj_index = duk_require_normalize_index(ctx, obj_index); - duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); - return duk_has_prop(ctx, obj_index); +DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_has_prop(thr, obj_idx); } -/* Define own property without inheritance looks and such. This differs from +DUK_EXTERNAL duk_bool_t duk_has_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + (void) duk_push_heapptr(thr, ptr); /* NULL -> 'undefined' */ + return duk_has_prop(thr, obj_idx); +} + +DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); + return duk_has_prop(thr, obj_idx); +} + +#if 0 +DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + return duk_has_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16), + (duk_small_uint_t) (packed_args & 0xffffUL)); +} +#endif + +/* Define own property without inheritance lookups and such. This differs from * [[DefineOwnProperty]] because special behaviors (like Array 'length') are * not invoked by this method. The caller must be careful to invoke any such * behaviors if necessary. */ -DUK_INTERNAL void duk_xdef_prop(duk_context *ctx, duk_idx_t obj_index, duk_small_uint_t desc_flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags) { duk_hobject *obj; duk_hstring *key; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_index); + obj = duk_require_hobject(thr, obj_idx); DUK_ASSERT(obj != NULL); - key = duk_to_hstring(ctx, -2); + key = duk_to_property_key_hstring(thr, -2); DUK_ASSERT(key != NULL); - DUK_ASSERT(duk_require_tval(ctx, -1) != NULL); + DUK_ASSERT(duk_require_tval(thr, -1) != NULL); duk_hobject_define_property_internal(thr, obj, key, desc_flags); - duk_pop(ctx); /* pop key */ + duk_pop(thr); /* pop key */ } -DUK_INTERNAL void duk_xdef_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index, duk_small_uint_t desc_flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags) { duk_hobject *obj; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_index); + obj = duk_require_hobject(thr, obj_idx); DUK_ASSERT(obj != NULL); - duk_hobject_define_property_internal_arridx(thr, obj, arr_index, desc_flags); + duk_hobject_define_property_internal_arridx(thr, obj, arr_idx, desc_flags); /* value popped by call */ } -DUK_INTERNAL void duk_xdef_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags) { duk_hobject *obj; duk_hstring *key; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); - obj = duk_require_hobject(ctx, obj_index); + obj = duk_require_hobject(thr, obj_idx); DUK_ASSERT(obj != NULL); key = DUK_HTHREAD_GET_STRING(thr, stridx); DUK_ASSERT(key != NULL); - DUK_ASSERT(duk_require_tval(ctx, -1) != NULL); + DUK_ASSERT(duk_require_tval(thr, -1) != NULL); duk_hobject_define_property_internal(thr, obj, key, desc_flags); /* value popped by call */ } -DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) { + duk_xdef_prop_stridx(thr, (duk_idx_t) (duk_int8_t) (packed_args >> 24), + (duk_small_uint_t) (packed_args >> 8) & 0xffffUL, + (duk_small_uint_t) (packed_args & 0xffL)); +} + +#if 0 /*unused*/ +DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) { duk_hobject *obj; duk_hstring *key; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(stridx >= 0); - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); - DUK_ASSERT_DISABLE(builtin_idx >= 0); - DUK_ASSERT(builtin_idx < DUK_NUM_BUILTINS); + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + DUK_ASSERT_BIDX_VALID(builtin_idx); - obj = duk_require_hobject(ctx, obj_index); + obj = duk_require_hobject(thr, obj_idx); DUK_ASSERT(obj != NULL); key = DUK_HTHREAD_GET_STRING(thr, stridx); DUK_ASSERT(key != NULL); - duk_push_hobject(ctx, thr->builtins[builtin_idx]); + duk_push_hobject(thr, thr->builtins[builtin_idx]); duk_hobject_define_property_internal(thr, obj, key, desc_flags); /* value popped by call */ } +#endif /* This is a rare property helper; it sets the global thrower (E5 Section 13.2.3) * setter/getter into an object property. This is needed by the 'arguments' * object creation code, function instance creation code, and Function.prototype.bind(). */ -DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *obj = duk_require_hobject(ctx, obj_index); - duk_hobject *thrower = thr->builtins[DUK_BIDX_TYPE_ERROR_THROWER]; - duk_hobject_define_accessor_internal(thr, obj, DUK_HTHREAD_GET_STRING(thr, stridx), thrower, thrower, desc_flags); +DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_hstring_stridx(thr, stridx); + duk_push_hobject_bidx(thr, DUK_BIDX_TYPE_ERROR_THROWER); + duk_dup_top(thr); + duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE); /* attributes always 0 */ +} + +/* Object.getOwnPropertyDescriptor() equivalent C binding. */ +DUK_EXTERNAL void duk_get_prop_desc(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(flags); /* no flags defined yet */ + + duk_hobject_object_get_own_property_descriptor(thr, obj_idx); /* [ ... key ] -> [ ... desc ] */ } /* Object.defineProperty() equivalent C binding. */ -DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_def_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) { duk_idx_t idx_base; duk_hobject *obj; duk_hstring *key; @@ -14681,9 +17451,9 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t duk_uint_t is_data_desc; duk_uint_t is_acc_desc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_index); + obj = duk_require_hobject(thr, obj_idx); is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE); is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); @@ -14695,12 +17465,12 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t goto fail_invalid_desc; } - idx_base = duk_get_top_index(ctx); + idx_base = duk_get_top_index(thr); if (flags & DUK_DEFPROP_HAVE_SETTER) { - duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED | + duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC); - set = duk_get_hobject_or_lfunc_coerce(ctx, idx_base); + set = duk_get_hobject_promote_lfunc(thr, idx_base); if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) { goto fail_not_callable; } @@ -14709,10 +17479,10 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t set = NULL; } if (flags & DUK_DEFPROP_HAVE_GETTER) { - duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED | + duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC); - get = duk_get_hobject_or_lfunc_coerce(ctx, idx_base); + get = duk_get_hobject_promote_lfunc(thr, idx_base); if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) { goto fail_not_callable; } @@ -14726,21 +17496,23 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t } else { idx_value = (duk_idx_t) -1; } - key = duk_require_hstring(ctx, idx_base); + key = duk_to_property_key_hstring(thr, idx_base); + DUK_ASSERT(key != NULL); - duk_require_valid_index(ctx, idx_base); + duk_require_valid_index(thr, idx_base); - duk_hobject_define_property_helper(ctx, + duk_hobject_define_property_helper(thr, flags /*defprop_flags*/, obj, key, idx_value, get, - set); + set, + 1 /*throw_flag*/); /* Clean up stack */ - duk_set_top(ctx, idx_base); + duk_set_top(thr, idx_base); /* [ ... obj ... ] */ @@ -14748,80 +17520,152 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t fail_invalid_desc: DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR); - return; + DUK_WO_NORETURN(return;); fail_not_callable: DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); - return; + DUK_WO_NORETURN(return;); } /* * Object related - * - * Note: seal() and freeze() are accessible through Ecmascript bindings, - * and are not exposed through the API. */ -DUK_EXTERNAL void duk_compact(duk_context *ctx, duk_idx_t obj_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_compact(duk_hthread *thr, duk_idx_t obj_idx) { duk_hobject *obj; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, obj_index); + obj = duk_get_hobject(thr, obj_idx); if (obj) { /* Note: this may fail, caller should protect the call if necessary */ duk_hobject_compact_props(thr, obj); } } -/* XXX: the duk_hobject_enum.c stack APIs should be reworked */ +DUK_INTERNAL void duk_compact_m1(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); -DUK_EXTERNAL void duk_enum(duk_context *ctx, duk_idx_t obj_index, duk_uint_t enum_flags) { - DUK_ASSERT_CTX_VALID(ctx); - - duk_dup(ctx, obj_index); - duk_require_hobject_or_lfunc_coerce(ctx, -1); - duk_hobject_enumerator_create(ctx, enum_flags); /* [target] -> [enum] */ + duk_compact(thr, -1); } -DUK_EXTERNAL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_index, duk_bool_t get_value) { - DUK_ASSERT_CTX_VALID(ctx); +/* XXX: the duk_hobject_enum.c stack APIs should be reworked */ - duk_require_hobject(ctx, enum_index); - duk_dup(ctx, enum_index); - return duk_hobject_enumerator_next(ctx, get_value); +DUK_EXTERNAL void duk_enum(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t enum_flags) { + DUK_ASSERT_API_ENTRY(thr); + + duk_dup(thr, obj_idx); + duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); + duk_hobject_enumerator_create(thr, enum_flags); /* [target] -> [enum] */ +} + +DUK_EXTERNAL duk_bool_t duk_next(duk_hthread *thr, duk_idx_t enum_index, duk_bool_t get_value) { + DUK_ASSERT_API_ENTRY(thr); + + duk_require_hobject(thr, enum_index); + duk_dup(thr, enum_index); + return duk_hobject_enumerator_next(thr, get_value); +} + +DUK_INTERNAL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze) { + duk_tval *tv; + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_require_tval(thr, obj_idx); + DUK_ASSERT(tv != NULL); + + /* Seal/freeze are quite rare in practice so it'd be nice to get the + * correct behavior simply via automatic promotion (at the cost of some + * memory churn). However, the promoted objects don't behave the same, + * e.g. promoted lightfuncs are extensible. + */ + + switch (DUK_TVAL_GET_TAG(tv)) { + case DUK_TAG_BUFFER: + /* Plain buffer: already sealed, but not frozen (and can't be frozen + * because index properties can't be made non-writable. + */ + if (is_freeze) { + goto fail_cannot_freeze; + } + break; + case DUK_TAG_LIGHTFUNC: + /* Lightfunc: already sealed and frozen, success. */ + break; + case DUK_TAG_OBJECT: + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) { + /* Buffer objects cannot be frozen because there's no internal + * support for making virtual array indices non-writable. + */ + DUK_DD(DUK_DDPRINT("cannot freeze a buffer object")); + goto fail_cannot_freeze; + } + duk_hobject_object_seal_freeze_helper(thr, h, is_freeze); + + /* Sealed and frozen objects cannot gain any more properties, + * so this is a good time to compact them. + */ + duk_hobject_compact_props(thr, h); + break; + default: + /* ES2015 Sections 19.1.2.5, 19.1.2.17 */ + break; + } + return; + + fail_cannot_freeze: + DUK_ERROR_TYPE_INVALID_ARGS(thr); /* XXX: proper error message */ + DUK_WO_NORETURN(return;); +} + +DUK_EXTERNAL void duk_seal(duk_hthread *thr, duk_idx_t obj_idx) { + DUK_ASSERT_API_ENTRY(thr); + + duk_seal_freeze_raw(thr, obj_idx, 0 /*is_freeze*/); +} + +DUK_EXTERNAL void duk_freeze(duk_hthread *thr, duk_idx_t obj_idx) { + DUK_ASSERT_API_ENTRY(thr); + + duk_seal_freeze_raw(thr, obj_idx, 1 /*is_freeze*/); } /* * Helpers for writing multiple properties */ -DUK_EXTERNAL void duk_put_function_list(duk_context *ctx, duk_idx_t obj_index, const duk_function_list_entry *funcs) { +DUK_EXTERNAL void duk_put_function_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_function_list_entry *funcs) { const duk_function_list_entry *ent = funcs; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj_index = duk_require_normalize_index(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); if (ent != NULL) { while (ent->key != NULL) { - duk_push_c_function(ctx, ent->value, ent->nargs); - duk_put_prop_string(ctx, obj_index, ent->key); + duk_push_c_function(thr, ent->value, ent->nargs); + duk_put_prop_string(thr, obj_idx, ent->key); ent++; } } } -DUK_EXTERNAL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_index, const duk_number_list_entry *numbers) { +DUK_EXTERNAL void duk_put_number_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_number_list_entry *numbers) { const duk_number_list_entry *ent = numbers; + duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj_index = duk_require_normalize_index(ctx, obj_index); + obj_idx = duk_require_normalize_index(thr, obj_idx); if (ent != NULL) { while (ent->key != NULL) { - duk_push_number(ctx, ent->value); - duk_put_prop_string(ctx, obj_index, ent->key); + tv = thr->valstack_top++; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); /* value stack init policy */ + DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, ent->value); /* no need for decref/incref */ + duk_put_prop_string(thr, obj_idx, ent->key); ent++; } } @@ -14831,108 +17675,301 @@ DUK_EXTERNAL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_index, con * Shortcut for accessing global object properties */ -DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_context *ctx, const char *key) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_hthread *thr, const char *key) { duk_bool_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); /* XXX: direct implementation */ - duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]); - ret = duk_get_prop_string(ctx, -1, key); - duk_remove(ctx, -2); + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + ret = duk_get_prop_string(thr, -1, key); + duk_remove_m2(thr); return ret; } -DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_context *ctx, const char *key) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_get_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) { duk_bool_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); /* XXX: direct implementation */ - duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]); - duk_insert(ctx, -2); - ret = duk_put_prop_string(ctx, -2, key); /* [ ... global val ] -> [ ... global ] */ - duk_pop(ctx); + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + ret = duk_get_prop_lstring(thr, -1, key, key_len); + duk_remove_m2(thr); return ret; } +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_get_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + DUK_ASSERT(key[key_len] == (char) 0); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + ret = duk_get_prop_literal_raw(thr, -1, key, key_len); + duk_remove_m2(thr); + return ret; +} +#endif + +DUK_EXTERNAL duk_bool_t duk_get_global_heapptr(duk_hthread *thr, void *ptr) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + ret = duk_get_prop_heapptr(thr, -1, ptr); + duk_remove_m2(thr); + return ret; +} + + +DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_hthread *thr, const char *key) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + duk_insert(thr, -2); + ret = duk_put_prop_string(thr, -2, key); /* [ ... global val ] -> [ ... global ] */ + duk_pop(thr); + return ret; +} + +DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + duk_insert(thr, -2); + ret = duk_put_prop_lstring(thr, -2, key, key_len); /* [ ... global val ] -> [ ... global ] */ + duk_pop(thr); + return ret; +} + +#if !defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL duk_bool_t duk_put_global_literal_raw(duk_hthread *thr, const char *key, duk_size_t key_len) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + DUK_ASSERT(key[key_len] == (char) 0); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + duk_insert(thr, -2); + ret = duk_put_prop_literal_raw(thr, -2, key, key_len); /* [ ... global val ] -> [ ... global ] */ + duk_pop(thr); + return ret; +} +#endif + +DUK_EXTERNAL duk_bool_t duk_put_global_heapptr(duk_hthread *thr, void *ptr) { + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); + + /* XXX: direct implementation */ + + duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]); + duk_insert(thr, -2); + ret = duk_put_prop_heapptr(thr, -2, ptr); /* [ ... global val ] -> [ ... global ] */ + duk_pop(thr); + return ret; +} + +/* + * ES2015 GetMethod() + */ + +DUK_INTERNAL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx) { + (void) duk_get_prop_stridx(thr, idx, stridx); + if (duk_is_null_or_undefined(thr, -1)) { + duk_pop_nodecref_unsafe(thr); + return 0; + } + if (!duk_is_callable(thr, -1)) { + DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); + DUK_WO_NORETURN(return 0;); + } + return 1; +} + /* * Object prototype */ -DUK_EXTERNAL void duk_get_prototype(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_get_prototype(duk_hthread *thr, duk_idx_t idx) { duk_hobject *obj; duk_hobject *proto; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, index); + obj = duk_require_hobject(thr, idx); DUK_ASSERT(obj != NULL); /* XXX: shared helper for duk_push_hobject_or_undefined()? */ proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj); if (proto) { - duk_push_hobject(ctx, proto); + duk_push_hobject(thr, proto); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_set_prototype(duk_hthread *thr, duk_idx_t idx) { duk_hobject *obj; duk_hobject *proto; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, index); + obj = duk_require_hobject(thr, idx); DUK_ASSERT(obj != NULL); - duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_UNDEFINED | + duk_require_type_mask(thr, -1, DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_OBJECT); - proto = duk_get_hobject(ctx, -1); + proto = duk_get_hobject(thr, -1); /* proto can also be NULL here (allowed explicitly) */ #if defined(DUK_USE_ROM_OBJECTS) if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); /* XXX: "read only object"? */ - return; + DUK_WO_NORETURN(return;); } #endif DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto); - duk_pop(ctx); + duk_pop(thr); +} + +DUK_INTERNAL void duk_clear_prototype(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *obj; + + DUK_ASSERT_API_ENTRY(thr); + + obj = duk_require_hobject(thr, idx); + DUK_ASSERT(obj != NULL); + +#if defined(DUK_USE_ROM_OBJECTS) + if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { + DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); /* XXX: "read only object"? */ + DUK_WO_NORETURN(return;); + } +#endif + + DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, NULL); +} + +DUK_INTERNAL duk_bool_t duk_is_bare_object(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *obj; + duk_hobject *proto; + + DUK_ASSERT_API_ENTRY(thr); + + obj = duk_require_hobject(thr, idx); + DUK_ASSERT(obj != NULL); + + proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj); + return (proto == NULL); } /* * Object finalizer */ +#if defined(DUK_USE_FINALIZER_SUPPORT) /* XXX: these could be implemented as macros calling an internal function * directly. * XXX: same issue as with Duktape.fin: there's no way to delete the property * now (just set it to undefined). */ -DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - duk_get_prop_stridx(ctx, index, DUK_STRIDX_INT_FINALIZER); + /* This get intentionally walks the inheritance chain at present, + * which matches how the effective finalizer property is also + * looked up in GC. + */ + duk_get_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER); } -DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + duk_bool_t callable; - duk_put_prop_stridx(ctx, index, DUK_STRIDX_INT_FINALIZER); + DUK_ASSERT_API_ENTRY(thr); + + h = duk_require_hobject(thr, idx); /* Get before 'put' so that 'idx' is correct. */ + callable = duk_is_callable(thr, -1); + + /* At present finalizer is stored as a hidden Symbol, with normal + * inheritance and access control. As a result, finalizer cannot + * currently be set on a non-extensible (sealed or frozen) object. + * It might be useful to allow it. + */ + duk_put_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER); + + /* In addition to setting the finalizer property, keep a "have + * finalizer" flag in duk_hobject in sync so that refzero can do + * a very quick finalizer check by walking the prototype chain + * and checking the flag alone. (Note that this means that just + * setting _Finalizer on an object won't affect finalizer checks.) + * + * NOTE: if the argument is a Proxy object, this flag will be set + * on the Proxy, not the target. As a result, the target won't get + * a finalizer flag and the Proxy also won't be finalized as there's + * an explicit Proxy check in finalization now. + */ + if (callable) { + DUK_HOBJECT_SET_HAVE_FINALIZER(h); + } else { + DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h); + } +} +#else /* DUK_USE_FINALIZER_SUPPORT */ +DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} + +DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(idx); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_FINALIZER_SUPPORT */ +/* + * Random numbers + */ + +/* #include duk_internal.h -> already included */ + +DUK_EXTERNAL duk_double_t duk_random(duk_hthread *thr) { + return (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE(thr); } -#line 1 "duk_api_stack.c" /* * API calls related to general value stack manipulation: resizing the value * stack, pushing and popping values, type checking and reading values, @@ -14945,19 +17982,19 @@ DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t index) { /* XXX: repetition of stack pre-checks -> helper or macro or inline */ /* XXX: shared api error strings, and perhaps even throw code for rare cases? */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* * Forward declarations */ -DUK_LOCAL_DECL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags); +DUK_LOCAL_DECL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx); /* * Global state for working around missing variadic macros */ -#ifndef DUK_USE_VARIADIC_MACROS +#if !defined(DUK_USE_VARIADIC_MACROS) DUK_EXTERNAL const char *duk_api_global_filename = NULL; DUK_EXTERNAL duk_int_t duk_api_global_line = 0; #endif @@ -14966,34 +18003,99 @@ DUK_EXTERNAL duk_int_t duk_api_global_line = 0; * Misc helpers */ +DUK_LOCAL const char * const duk__symbol_type_strings[4] = { + "hidden", "global", "local", "wellknown" +}; + +#if !defined(DUK_USE_PACKED_TVAL) +DUK_LOCAL const duk_uint_t duk__type_from_tag[] = { + DUK_TYPE_NUMBER, + DUK_TYPE_NUMBER, /* fastint */ + DUK_TYPE_UNDEFINED, + DUK_TYPE_NULL, + DUK_TYPE_BOOLEAN, + DUK_TYPE_POINTER, + DUK_TYPE_LIGHTFUNC, + DUK_TYPE_NONE, + DUK_TYPE_STRING, + DUK_TYPE_OBJECT, + DUK_TYPE_BUFFER, +}; +DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = { + DUK_TYPE_MASK_NUMBER, + DUK_TYPE_MASK_NUMBER, /* fastint */ + DUK_TYPE_MASK_UNDEFINED, + DUK_TYPE_MASK_NULL, + DUK_TYPE_MASK_BOOLEAN, + DUK_TYPE_MASK_POINTER, + DUK_TYPE_MASK_LIGHTFUNC, + DUK_TYPE_MASK_NONE, + DUK_TYPE_MASK_STRING, + DUK_TYPE_MASK_OBJECT, + DUK_TYPE_MASK_BUFFER, +}; +#endif /* !DUK_USE_PACKED_TVAL */ + +/* Assert that there's room for one value. */ +#define DUK__ASSERT_SPACE() do { \ + DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \ + } while (0) + /* Check that there's room to push one value. */ #if defined(DUK_USE_VALSTACK_UNSAFE) /* Faster but value stack overruns are memory unsafe. */ -#define DUK__CHECK_SPACE() do { \ - DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \ - } while (0) +#define DUK__CHECK_SPACE() DUK__ASSERT_SPACE() #else #define DUK__CHECK_SPACE() do { \ if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \ - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); \ + DUK_ERROR_RANGE_PUSH_BEYOND(thr); \ } \ } while (0) #endif -DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t index, duk_uint_t tag); +DUK_LOCAL duk_small_uint_t duk__get_symbol_type(duk_hstring *h) { + const duk_uint8_t *data; + duk_size_t len; -DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t index, duk_bool_t require) { - duk_hthread *thr; + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HSTRING_HAS_SYMBOL(h)); + DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h) >= 1); /* always true, symbol prefix */ + + data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); + len = DUK_HSTRING_GET_BYTELEN(h); + DUK_ASSERT(len >= 1); + + /* XXX: differentiate between 0x82 and 0xff (hidden vs. internal?)? */ + + if (data[0] == 0xffU) { + return DUK_SYMBOL_TYPE_HIDDEN; + } else if (data[0] == 0x82U) { + return DUK_SYMBOL_TYPE_HIDDEN; + } else if (data[0] == 0x80U) { + return DUK_SYMBOL_TYPE_GLOBAL; + } else if (data[len - 1] != 0xffU) { + return DUK_SYMBOL_TYPE_LOCAL; + } else { + return DUK_SYMBOL_TYPE_WELLKNOWN; + } +} + +DUK_LOCAL const char *duk__get_symbol_type_string(duk_hstring *h) { + duk_small_uint_t idx; + idx = duk__get_symbol_type(h); + DUK_ASSERT(idx < sizeof(duk__symbol_type_strings)); + return duk__symbol_type_strings[idx]; +} + +DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag); + +DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value, duk_bool_t require) { duk_tval *tv; duk_small_int_t c; duk_double_t d; - thr = (duk_hthread *) ctx; - - tv = duk_get_tval(ctx, index); - if (tv == NULL) { - goto error_notnumber; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); /* * Special cases like NaN and +/- Infinity are handled explicitly @@ -15040,29 +18142,23 @@ DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t index, duk_b } } - error_notnumber: - if (require) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER); - /* not reachable */ + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); + DUK_WO_NORETURN(return 0;); } - return 0; + + return def_value; } -DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t index, duk_bool_t require) { - duk_hthread *thr; +DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value, duk_bool_t require) { duk_tval *tv; duk_small_int_t c; duk_double_t d; /* Same as above but for unsigned int range. */ - thr = (duk_hthread *) ctx; - - tv = duk_get_tval(ctx, index); - if (tv == NULL) { - goto error_notnumber; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); #if defined(DUK_USE_FASTINT) if (DUK_TVAL_IS_FASTINT(tv)) { @@ -15097,13 +18193,12 @@ DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t index, duk } } - error_notnumber: - if (require) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER); - /* not reachable */ + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); + DUK_WO_NORETURN(return 0;); } - return 0; + + return def_value; } /* @@ -15115,12 +18210,11 @@ DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t index, duk * There's some repetition because of this; keep the functions in sync. */ -DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_hthread *thr, duk_idx_t idx) { duk_uidx_t vs_size; - duk_uidx_t uindex; + duk_uidx_t uidx; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); /* Care must be taken to avoid pointer wrapping in the index @@ -15134,86 +18228,103 @@ DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t index) { vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - if (index < 0) { - uindex = vs_size + (duk_uidx_t) index; + if (idx < 0) { + uidx = vs_size + (duk_uidx_t) idx; } else { /* since index non-negative */ - DUK_ASSERT(index != DUK_INVALID_INDEX); - uindex = (duk_uidx_t) index; + DUK_ASSERT(idx != DUK_INVALID_INDEX); + uidx = (duk_uidx_t) idx; } /* DUK_INVALID_INDEX won't be accepted as a valid index. */ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - if (DUK_LIKELY(uindex < vs_size)) { - return (duk_idx_t) uindex; + if (DUK_LIKELY(uidx < vs_size)) { + return (duk_idx_t) uidx; } return DUK_INVALID_INDEX; } -DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_hthread *thr, duk_idx_t idx) { duk_uidx_t vs_size; - duk_uidx_t uindex; + duk_uidx_t uidx; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - if (index < 0) { - uindex = vs_size + (duk_uidx_t) index; + if (idx < 0) { + uidx = vs_size + (duk_uidx_t) idx; } else { - DUK_ASSERT(index != DUK_INVALID_INDEX); - uindex = (duk_uidx_t) index; + DUK_ASSERT(idx != DUK_INVALID_INDEX); + uidx = (duk_uidx_t) idx; } /* DUK_INVALID_INDEX won't be accepted as a valid index. */ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - if (DUK_LIKELY(uindex < vs_size)) { - return (duk_idx_t) uindex; + if (DUK_LIKELY(uidx < vs_size)) { + return (duk_idx_t) uidx; } - DUK_ERROR_API_INDEX(thr, index); - return 0; /* unreachable */ + DUK_ERROR_RANGE_INDEX(thr, idx); + DUK_WO_NORETURN(return 0;); } -DUK_INTERNAL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx) { duk_uidx_t vs_size; - duk_uidx_t uindex; + duk_uidx_t uidx; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ - if (index < 0) { - uindex = vs_size + (duk_uidx_t) index; + if (idx < 0) { + uidx = vs_size + (duk_uidx_t) idx; } else { - DUK_ASSERT(index != DUK_INVALID_INDEX); - uindex = (duk_uidx_t) index; + DUK_ASSERT(idx != DUK_INVALID_INDEX); + uidx = (duk_uidx_t) idx; } /* DUK_INVALID_INDEX won't be accepted as a valid index. */ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - if (DUK_LIKELY(uindex < vs_size)) { - return thr->valstack_bottom + uindex; + if (DUK_LIKELY(uidx < vs_size)) { + return thr->valstack_bottom + uidx; } return NULL; } -DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_uidx_t vs_size; - duk_uidx_t uindex; +/* Variant of duk_get_tval() which is guaranteed to return a valid duk_tval + * pointer. When duk_get_tval() would return NULL, this variant returns a + * pointer to a duk_tval with tag DUK_TAG_UNUSED. This allows the call site + * to avoid an unnecessary NULL check which sometimes leads to better code. + * The return duk_tval is read only (at least for the UNUSED value). + */ +DUK_LOCAL const duk_tval_unused duk__const_tval_unused = DUK_TVAL_UNUSED_INITIALIZER(); - DUK_ASSERT_CTX_VALID(ctx); +DUK_INTERNAL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval(thr, idx); + if (tv != NULL) { + return tv; + } + return (duk_tval *) DUK_LOSE_CONST(&duk__const_tval_unused); +} + +DUK_INTERNAL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx) { + duk_uidx_t vs_size; + duk_uidx_t uidx; + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); @@ -15221,41 +18332,39 @@ DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t index) { DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */ /* Use unsigned arithmetic to optimize comparison. */ - if (index < 0) { - uindex = vs_size + (duk_uidx_t) index; + if (idx < 0) { + uidx = vs_size + (duk_uidx_t) idx; } else { - DUK_ASSERT(index != DUK_INVALID_INDEX); - uindex = (duk_uidx_t) index; + DUK_ASSERT(idx != DUK_INVALID_INDEX); + uidx = (duk_uidx_t) idx; } /* DUK_INVALID_INDEX won't be accepted as a valid index. */ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size); - if (DUK_LIKELY(uindex < vs_size)) { - return thr->valstack_bottom + uindex; + if (DUK_LIKELY(uidx < vs_size)) { + return thr->valstack_bottom + uidx; } - DUK_ERROR_API_INDEX(thr, index); - return NULL; + DUK_ERROR_RANGE_INDEX(thr, idx); + DUK_WO_NORETURN(return NULL;); } /* Non-critical. */ -DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); - return (duk_normalize_index(ctx, index) >= 0); + return (duk_normalize_index(thr, idx) >= 0); } /* Non-critical. */ -DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_require_valid_index(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); - if (duk_normalize_index(ctx, index) < 0) { - DUK_ERROR_API_INDEX(thr, index); - return; /* unreachable */ + if (DUK_UNLIKELY(duk_normalize_index(thr, idx) < 0)) { + DUK_ERROR_RANGE_INDEX(thr, idx); + DUK_WO_NORETURN(return;); } } @@ -15263,26 +18372,39 @@ DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t index) { * Value stack top handling */ -DUK_EXTERNAL duk_idx_t duk_get_top(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_idx_t duk_get_top(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); } +/* Internal helper to get current top but to require a minimum top value + * (TypeError if not met). + */ +DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top) { + duk_idx_t ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + if (DUK_UNLIKELY(ret < min_top)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); + } + return ret; +} + /* Set stack top within currently allocated range, but don't reallocate. * This is performance critical especially for call handling, so whenever * changing, profile and look at generated code. */ -DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_set_top(duk_hthread *thr, duk_idx_t idx) { duk_uidx_t vs_size; duk_uidx_t vs_limit; - duk_uidx_t uindex; + duk_uidx_t uidx; duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_INVALID_INDEX < 0); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); @@ -15290,16 +18412,16 @@ DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) { vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); vs_limit = (duk_uidx_t) (thr->valstack_end - thr->valstack_bottom); - if (index < 0) { + if (idx < 0) { /* Negative indices are always within allocated stack but * must not go below zero index. */ - uindex = vs_size + (duk_uidx_t) index; + uidx = vs_size + (duk_uidx_t) idx; } else { /* Positive index can be higher than valstack top but must * not go above allocated stack (equality is OK). */ - uindex = (duk_uidx_t) index; + uidx = (duk_uidx_t) idx; } /* DUK_INVALID_INDEX won't be accepted as a valid index. */ @@ -15307,15 +18429,15 @@ DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) { DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_limit); #if defined(DUK_USE_VALSTACK_UNSAFE) - DUK_ASSERT(uindex <= vs_limit); + DUK_ASSERT(uidx <= vs_limit); DUK_UNREF(vs_limit); #else - if (DUK_UNLIKELY(uindex > vs_limit)) { - DUK_ERROR_API_INDEX(thr, index); - return; /* unreachable */ + if (DUK_UNLIKELY(uidx > vs_limit)) { + DUK_ERROR_RANGE_INDEX(thr, idx); + DUK_WO_NORETURN(return;); } #endif - DUK_ASSERT(uindex <= vs_limit); + DUK_ASSERT(uidx <= vs_limit); /* Handle change in value stack top. Respect value stack * initialization policy: 'undefined' above top. Note that @@ -15323,37 +18445,42 @@ DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) { * so must relookup after DECREF. */ - if (uindex >= vs_size) { + if (uidx >= vs_size) { /* Stack size increases or stays the same. */ #if defined(DUK_USE_ASSERTIONS) duk_uidx_t count; - count = uindex - vs_size; + count = uidx - vs_size; while (count != 0) { count--; tv = thr->valstack_top + count; DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); } #endif - thr->valstack_top = thr->valstack_bottom + uindex; + thr->valstack_top = thr->valstack_bottom + uidx; } else { /* Stack size decreases. */ #if defined(DUK_USE_REFERENCE_COUNTING) duk_uidx_t count; + duk_tval *tv_end; - count = vs_size - uindex; + count = vs_size - uidx; DUK_ASSERT(count > 0); - while (count > 0) { - count--; - tv = --thr->valstack_top; /* tv -> value just before prev top value; must relookup */ + tv = thr->valstack_top; + tv_end = tv - count; + DUK_ASSERT(tv > tv_end); /* Because count > 0. */ + do { + tv--; DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ - } + DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); + } while (tv != tv_end); + thr->valstack_top = tv_end; + DUK_REFZERO_CHECK_FAST(thr); #else /* DUK_USE_REFERENCE_COUNTING */ duk_uidx_t count; duk_tval *tv_end; - count = vs_size - uindex; + count = vs_size - uidx; tv = thr->valstack_top; tv_end = tv - count; DUK_ASSERT(tv > tv_end); @@ -15366,13 +18493,100 @@ DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) { } } -DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +/* Internal variant with a non-negative index and no runtime size checks. */ +#if defined(DUK_USE_PREFER_SIZE) +DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + duk_set_top(thr, idx); +} +#else /* DUK_USE_PREFER_SIZE */ +DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) { + duk_uidx_t uidx; + duk_uidx_t vs_size; + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom); + DUK_ASSERT(idx >= 0); + DUK_ASSERT(idx <= (duk_idx_t) (thr->valstack_end - thr->valstack_bottom)); + + /* XXX: byte arithmetic */ + uidx = (duk_uidx_t) idx; + vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom); + + if (uidx >= vs_size) { + /* Stack size increases or stays the same. */ +#if defined(DUK_USE_ASSERTIONS) + duk_uidx_t count; + + count = uidx - vs_size; + while (count != 0) { + count--; + tv = thr->valstack_top + count; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); + } +#endif + thr->valstack_top = thr->valstack_bottom + uidx; + } else { + /* Stack size decreases. */ +#if defined(DUK_USE_REFERENCE_COUNTING) + duk_uidx_t count; + duk_tval *tv_end; + + count = vs_size - uidx; + DUK_ASSERT(count > 0); + tv = thr->valstack_top; + tv_end = tv - count; + DUK_ASSERT(tv > tv_end); /* Because count > 0. */ + do { + tv--; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); + } while (tv != tv_end); + thr->valstack_top = tv_end; + DUK_REFZERO_CHECK_FAST(thr); +#else /* DUK_USE_REFERENCE_COUNTING */ + duk_uidx_t count; + duk_tval *tv_end; + + count = vs_size - uidx; + tv = thr->valstack_top; + tv_end = tv - count; + DUK_ASSERT(tv > tv_end); + do { + tv--; + DUK_TVAL_SET_UNDEFINED(tv); + } while (tv != tv_end); + thr->valstack_top = tv_end; +#endif /* DUK_USE_REFERENCE_COUNTING */ + } +} +#endif /* DUK_USE_PREFER_SIZE */ + +/* Internal helper: set top to 'top', and set [idx_wipe_start,top[ to + * 'undefined' (doing nothing if idx_wipe_start == top). Indices are + * positive and within value stack reserve. This is used by call handling. + */ +DUK_INTERNAL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(top >= 0); + DUK_ASSERT(idx_wipe_start >= 0); + DUK_ASSERT(idx_wipe_start <= top); + DUK_ASSERT(thr->valstack_bottom + top <= thr->valstack_end); + DUK_ASSERT(thr->valstack_bottom + idx_wipe_start <= thr->valstack_end); + + duk_set_top_unsafe(thr, idx_wipe_start); + duk_set_top_unsafe(thr, top); +} + +DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_hthread *thr) { duk_idx_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1; + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; if (DUK_UNLIKELY(ret < 0)) { /* Return invalid index; if caller uses this without checking * in another API call, the index won't map to a valid stack @@ -15383,16 +18597,27 @@ DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) { return ret; } -DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +/* Internal variant: call assumes there is at least one element on the value + * stack frame; this is only asserted for. + */ +DUK_INTERNAL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr) { duk_idx_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1; + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; + return ret; +} + +DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_hthread *thr) { + duk_idx_t ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1; if (DUK_UNLIKELY(ret < 0)) { - DUK_ERROR_API_INDEX(thr, -1); - return 0; /* unreachable */ + DUK_ERROR_RANGE_INDEX(thr, -1); + DUK_WO_NORETURN(return 0;); } return ret; } @@ -15402,64 +18627,74 @@ DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) { * * This resizing happens above the current "top": the value stack can be * grown or shrunk, but the "top" is not affected. The value stack cannot - * be resized to a size below the current "top". + * be resized to a size below the current reserve. * * The low level reallocation primitive must carefully recompute all value * stack pointers, and must also work if ALL pointers are NULL. The resize * is quite tricky because the valstack realloc may cause a mark-and-sweep, * which may run finalizers. Running finalizers may resize the valstack * recursively (the same value stack we're working on). So, after realloc - * returns, we know that the valstack "top" should still be the same (there - * should not be live values above the "top"), but its underlying size and - * pointer may have changed. + * returns, we know that the valstack bottom, top, and reserve should still + * be the same (there should not be live values above the "top"), but its + * underlying size, alloc_end, and base pointer may have changed. + * + * 'new_size' is known to be <= DUK_USE_VALSTACK_LIMIT, which ensures that + * size_t and pointer arithmetic won't wrap in duk__resize_valstack(). */ -/* XXX: perhaps refactor this to allow caller to specify some parameters, or - * at least a 'compact' flag which skips any spare or round-up .. useful for - * emergency gc. +/* Low level valstack resize primitive, used for both grow and shrink. All + * adjustments for slack etc have already been done. Doesn't throw but does + * have allocation side effects. */ - -DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_ptrdiff_t old_bottom_offset; - duk_ptrdiff_t old_top_offset; - duk_ptrdiff_t old_end_offset_post; -#ifdef DUK_USE_DEBUG - duk_ptrdiff_t old_end_offset_pre; - duk_tval *old_valstack_pre; - duk_tval *old_valstack_post; -#endif +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__resize_valstack(duk_hthread *thr, duk_size_t new_size) { + duk_tval *pre_valstack; + duk_tval *pre_bottom; + duk_tval *pre_top; + duk_tval *pre_end; + duk_tval *pre_alloc_end; + duk_ptrdiff_t ptr_diff; duk_tval *new_valstack; duk_size_t new_alloc_size; + duk_tval *tv_prev_alloc_end; duk_tval *p; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_HTHREAD_ASSERT_VALID(thr); DUK_ASSERT(thr->valstack_bottom >= thr->valstack); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size); /* can't resize below 'top' */ - DUK_ASSERT(new_size <= thr->valstack_max); /* valstack limit caller has check, prevents wrapping */ + DUK_ASSERT(new_size <= DUK_USE_VALSTACK_LIMIT); /* valstack limit caller has check, prevents wrapping */ DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval)); /* specific assert for wrapping */ - /* get pointer offsets for tweaking below */ - old_bottom_offset = (((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack)); - old_top_offset = (((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack)); -#ifdef DUK_USE_DEBUG - old_end_offset_pre = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* not very useful, used for debugging */ - old_valstack_pre = thr->valstack; + /* Pre-realloc pointer copies for asserts and debug logs. */ + pre_valstack = thr->valstack; + pre_bottom = thr->valstack_bottom; + pre_top = thr->valstack_top; + pre_end = thr->valstack_end; + pre_alloc_end = thr->valstack_alloc_end; + + DUK_UNREF(pre_valstack); + DUK_UNREF(pre_bottom); + DUK_UNREF(pre_top); + DUK_UNREF(pre_end); + DUK_UNREF(pre_alloc_end); + + /* If finalizer torture enabled, force base pointer change every time + * when it would be allowed. + */ +#if defined(DUK_USE_FINALIZER_TORTURE) + if (thr->heap->pf_prevent_count == 0) { + duk_hthread_valstack_torture_realloc(thr); + } #endif - /* Allocate a new valstack. - * - * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may - * invalidate the original thr->valstack base pointer inside the realloc - * process. See doc/memory-management.rst. + /* Allocate a new valstack using DUK_REALLOC_DIRECT() to deal with + * a side effect changing the base pointer. */ - new_alloc_size = sizeof(duk_tval) * new_size; new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size); - if (!new_valstack) { + if (DUK_UNLIKELY(new_valstack == NULL)) { /* Because new_size != 0, if condition doesn't need to be * (new_valstack != NULL || new_size == 0). */ @@ -15469,69 +18704,78 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) return 0; } - /* Note: the realloc may have triggered a mark-and-sweep which may - * have resized our valstack internally. However, the mark-and-sweep - * MUST NOT leave the stack bottom/top in a different state. Particular - * assumptions and facts: - * - * - The thr->valstack pointer may be different after realloc, - * and the offset between thr->valstack_end <-> thr->valstack - * may have changed. - * - The offset between thr->valstack_bottom <-> thr->valstack - * and thr->valstack_top <-> thr->valstack MUST NOT have changed, - * because mark-and-sweep must adhere to a strict stack policy. - * In other words, logical bottom and top MUST NOT have changed. - * - All values above the top are unreachable but are initialized - * to UNDEFINED, up to the post-realloc valstack_end. - * - 'old_end_offset' must be computed after realloc to be correct. + /* Debug log any changes in pointer(s) by side effects. These don't + * necessarily imply any incorrect behavior, but should be rare in + * practice. */ - - DUK_ASSERT((((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack)) == old_bottom_offset); - DUK_ASSERT((((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack)) == old_top_offset); - - /* success, fixup pointers */ - old_end_offset_post = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* must be computed after realloc */ -#ifdef DUK_USE_DEBUG - old_valstack_post = thr->valstack; +#if defined(DUK_USE_DEBUG) + if (thr->valstack != pre_valstack) { + DUK_D(DUK_DPRINT("valstack base pointer changed during valstack resize: %p -> %p", + (void *) pre_valstack, (void *) thr->valstack)); + } + if (thr->valstack_bottom != pre_bottom) { + DUK_D(DUK_DPRINT("valstack bottom pointer changed during valstack resize: %p -> %p", + (void *) pre_bottom, (void *) thr->valstack_bottom)); + } + if (thr->valstack_top != pre_top) { + DUK_D(DUK_DPRINT("valstack top pointer changed during valstack resize: %p -> %p", + (void *) pre_top, (void *) thr->valstack_top)); + } + if (thr->valstack_end != pre_end) { + DUK_D(DUK_DPRINT("valstack end pointer changed during valstack resize: %p -> %p", + (void *) pre_end, (void *) thr->valstack_end)); + } + if (thr->valstack_alloc_end != pre_alloc_end) { + DUK_D(DUK_DPRINT("valstack alloc_end pointer changed during valstack resize: %p -> %p", + (void *) pre_alloc_end, (void *) thr->valstack_alloc_end)); + } #endif + + /* Assertions: offsets for bottom, top, and end (reserve) must not + * have changed even with side effects because they are always + * restored in unwind. For alloc_end there's no guarantee: it may + * have grown or shrunk (but remain above 'end'). + */ + DUK_ASSERT(thr->valstack_bottom - thr->valstack == pre_bottom - pre_valstack); + DUK_ASSERT(thr->valstack_top - thr->valstack == pre_top - pre_valstack); + DUK_ASSERT(thr->valstack_end - thr->valstack == pre_end - pre_valstack); + DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); + + /* Write new pointers. Most pointers can be handled as a pointer + * difference. + */ + ptr_diff = (duk_ptrdiff_t) ((duk_uint8_t *) new_valstack - (duk_uint8_t *) thr->valstack); + tv_prev_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_alloc_end + ptr_diff); thr->valstack = new_valstack; - thr->valstack_end = new_valstack + new_size; -#if !defined(DUK_USE_PREFER_SIZE) - thr->valstack_size = new_size; -#endif - thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_bottom_offset); - thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_top_offset); + thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + ptr_diff); + thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + ptr_diff); + thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_end + ptr_diff); + thr->valstack_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + new_alloc_size); + /* Assertions: pointer sanity after pointer updates. */ DUK_ASSERT(thr->valstack_bottom >= thr->valstack); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); - /* useful for debugging */ -#ifdef DUK_USE_DEBUG - if (old_end_offset_pre != old_end_offset_post) { - DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; " - "end offset changed: %lu -> %lu", - (unsigned long) old_end_offset_pre, - (unsigned long) old_end_offset_post)); - } - if (old_valstack_pre != old_valstack_post) { - DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p", - (void *) old_valstack_pre, - (void *) old_valstack_post)); - } -#endif + DUK_D(DUK_DPRINT("resized valstack %lu -> %lu elements (%lu -> %lu bytes): " + "base=%p -> %p, bottom=%p -> %p (%ld), top=%p -> %p (%ld), " + "end=%p -> %p (%ld), alloc_end=%p -> %p (%ld);" + " tv_prev_alloc_end=%p (-> %ld inits; <0 means shrink)", + (unsigned long) (pre_alloc_end - pre_valstack), + (unsigned long) new_size, + (unsigned long) ((duk_uint8_t *) pre_alloc_end - (duk_uint8_t *) pre_valstack), + (unsigned long) new_alloc_size, + (void *) pre_valstack, (void *) thr->valstack, + (void *) pre_bottom, (void *) thr->valstack_bottom, (long) (thr->valstack_bottom - thr->valstack), + (void *) pre_top, (void *) thr->valstack_top, (long) (thr->valstack_top - thr->valstack), + (void *) pre_end, (void *) thr->valstack_end, (long) (thr->valstack_end - thr->valstack), + (void *) pre_alloc_end, (void *) thr->valstack_alloc_end, (long) (thr->valstack_alloc_end - thr->valstack), + (void *) tv_prev_alloc_end, (long) (thr->valstack_alloc_end - tv_prev_alloc_end))); - DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, " - "new pointers: start=%p end=%p bottom=%p top=%p", - (unsigned long) new_size, (unsigned long) new_alloc_size, - (long) (thr->valstack_bottom - thr->valstack), - (long) (thr->valstack_top - thr->valstack), - (void *) thr->valstack, (void *) thr->valstack_end, - (void *) thr->valstack_bottom, (void *) thr->valstack_top)); - - /* Init newly allocated slots (only). */ - p = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + old_end_offset_post); - while (p < thr->valstack_end) { + /* If allocation grew, init any new slots to 'undefined'. */ + p = tv_prev_alloc_end; + while (p < thr->valstack_alloc_end) { /* Never executed if new size is smaller. */ DUK_TVAL_SET_UNDEFINED(p); p++; @@ -15540,7 +18784,7 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) /* Assert for value stack initialization policy. */ #if defined(DUK_USE_ASSERTIONS) p = thr->valstack_top; - while (p < thr->valstack_end) { + while (p < thr->valstack_alloc_end) { DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p)); p++; } @@ -15549,204 +18793,259 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) return 1; } -DUK_INTERNAL -duk_bool_t duk_valstack_resize_raw(duk_context *ctx, - duk_size_t min_new_size, - duk_small_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_size_t old_size; +DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__valstack_grow(duk_hthread *thr, duk_size_t min_bytes, duk_bool_t throw_on_error) { + duk_size_t min_size; duk_size_t new_size; - duk_bool_t is_shrink = 0; - duk_small_uint_t shrink_flag = (flags & DUK_VSRESIZE_FLAG_SHRINK); - duk_small_uint_t compact_flag = (flags & DUK_VSRESIZE_FLAG_COMPACT); - duk_small_uint_t throw_flag = (flags & DUK_VSRESIZE_FLAG_THROW); - DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, " - "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d", - (unsigned long) min_new_size, - (long) (thr->valstack_end - thr->valstack), - (long) (thr->valstack_top - thr->valstack), - (long) (thr->valstack_bottom - thr->valstack), - (int) shrink_flag, (int) compact_flag, (int) throw_flag)); + DUK_ASSERT(min_bytes / sizeof(duk_tval) * sizeof(duk_tval) == min_bytes); + min_size = min_bytes / sizeof(duk_tval); /* from bytes to slots */ - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->valstack_bottom >= thr->valstack); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - -#if defined(DUK_USE_PREFER_SIZE) - old_size = (duk_size_t) (thr->valstack_end - thr->valstack); +#if defined(DUK_USE_VALSTACK_GROW_SHIFT) + /* New size is minimum size plus a proportional slack, e.g. shift of + * 2 means a 25% slack. + */ + new_size = min_size + (min_size >> DUK_USE_VALSTACK_GROW_SHIFT); #else - DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); - old_size = thr->valstack_size; + /* New size is tight with no slack. This is sometimes preferred in + * low memory environments. + */ + new_size = min_size; #endif - if (min_new_size <= old_size) { - is_shrink = 1; - if (!shrink_flag || - old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD) { - DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack")); - return 1; - } - } - - new_size = min_new_size; - if (!compact_flag) { - if (is_shrink) { - /* shrink case; leave some spare */ - new_size += DUK_VALSTACK_SHRINK_SPARE; - } - - /* round up roughly to next 'grow step' */ - new_size = (new_size / DUK_VALSTACK_GROW_STEP + 1) * DUK_VALSTACK_GROW_STEP; - } - - DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)", - (const char *) (new_size > old_size ? "grow" : "shrink"), - (unsigned long) old_size, (unsigned long) new_size, - (unsigned long) min_new_size)); - - if (new_size > thr->valstack_max) { + if (DUK_UNLIKELY(new_size > DUK_USE_VALSTACK_LIMIT || new_size < min_size /*wrap*/)) { /* Note: may be triggered even if minimal new_size would not reach the limit, - * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account). + * plan limit accordingly. */ - if (throw_flag) { + if (throw_on_error) { DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT); - } else { - return 0; + DUK_WO_NORETURN(return 0;); } + return 0; } - /* - * When resizing the valstack, a mark-and-sweep may be triggered for - * the allocation of the new valstack. If the mark-and-sweep needs - * to use our thread for something, it may cause *the same valstack* - * to be resized recursively. This happens e.g. when mark-and-sweep - * finalizers are called. This is taken into account carefully in - * duk__resize_valstack(). - * - * 'new_size' is known to be <= valstack_max, which ensures that - * size_t and pointer arithmetic won't wrap in duk__resize_valstack(). - */ - - if (!duk__resize_valstack(ctx, new_size)) { - if (is_shrink) { - DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore")); - return 1; - } - - DUK_DD(DUK_DDPRINT("valstack resize failed")); - - if (throw_flag) { - DUK_ERROR_ALLOC_DEFMSG(thr); - } else { - return 0; + if (duk__resize_valstack(thr, new_size) == 0) { + if (throw_on_error) { + DUK_ERROR_ALLOC_FAILED(thr); + DUK_WO_NORETURN(return 0;); } + return 0; } - DUK_DDD(DUK_DDDPRINT("valstack resize successful")); + thr->valstack_end = thr->valstack + min_size; + DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); + return 1; } -DUK_EXTERNAL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_size_t min_new_size; +/* Hot, inlined value stack grow check. Because value stack almost never + * grows, the actual resize call is in a NOINLINE helper. + */ +DUK_INTERNAL DUK_INLINE void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes) { + duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes); + if (DUK_LIKELY(thr->valstack_end >= tv)) { + return; + } + if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) { + /* Values in [valstack_top,valstack_alloc_end[ are initialized + * to 'undefined' so we can just move the end pointer. + */ + thr->valstack_end = tv; + return; + } + (void) duk__valstack_grow(thr, min_bytes, 1 /*throw_on_error*/); +} + +/* Hot, inlined value stack grow check which doesn't throw. */ +DUK_INTERNAL DUK_INLINE duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes) { + duk_tval *tv; + + tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes); + if (DUK_LIKELY(thr->valstack_end >= tv)) { + return 1; + } + if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) { + thr->valstack_end = tv; + return 1; + } + return duk__valstack_grow(thr, min_bytes, 0 /*throw_on_error*/); +} + +/* Value stack shrink check, called from mark-and-sweep. */ +DUK_INTERNAL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug) { + duk_size_t alloc_bytes; + duk_size_t reserve_bytes; + duk_size_t shrink_bytes; + + alloc_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack); + reserve_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack); + DUK_ASSERT(alloc_bytes >= reserve_bytes); + + /* We're free to shrink the value stack allocation down to + * reserve_bytes but not more. If 'snug' (emergency GC) + * shrink whatever we can. Otherwise only shrink if the new + * size would be considerably smaller. + */ + +#if defined(DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT) + if (snug) { + shrink_bytes = reserve_bytes; + } else { + duk_size_t proportion, slack; + + /* Require that value stack shrinks by at least X% of its + * current size. For example, shift of 2 means at least + * 25%. The proportion is computed as bytes and may not + * be a multiple of sizeof(duk_tval); that's OK here. + */ + proportion = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT; + if (alloc_bytes - reserve_bytes < proportion) { + /* Too little would be freed, do nothing. */ + return; + } + + /* Keep a slack after shrinking. The slack is again a + * proportion of the current size (the proportion should + * of course be smaller than the check proportion above). + */ +#if defined(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT) + DUK_ASSERT(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT > DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT); + slack = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT; +#else + slack = 0; +#endif + shrink_bytes = reserve_bytes + + slack / sizeof(duk_tval) * sizeof(duk_tval); /* multiple of duk_tval */ + } +#else /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */ + /* Always snug, useful in some low memory environments. */ + DUK_UNREF(snug); + shrink_bytes = reserve_bytes; +#endif /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */ + + DUK_D(DUK_DPRINT("valstack shrink check: alloc_bytes=%ld, reserve_bytes=%ld, shrink_bytes=%ld (unvalidated)", + (long) alloc_bytes, (long) reserve_bytes, (long) shrink_bytes)); + DUK_ASSERT(shrink_bytes >= reserve_bytes); + if (shrink_bytes >= alloc_bytes) { + /* Skip if shrink target is same as current one (or higher, + * though that shouldn't happen in practice). + */ + return; + } + DUK_ASSERT(shrink_bytes / sizeof(duk_tval) * sizeof(duk_tval) == shrink_bytes); + + DUK_D(DUK_DPRINT("valstack shrink check: decided to shrink, snug: %ld", (long) snug)); + + duk__resize_valstack(thr, shrink_bytes / sizeof(duk_tval)); +} + +DUK_EXTERNAL duk_bool_t duk_check_stack(duk_hthread *thr, duk_idx_t extra) { + duk_size_t min_new_bytes; + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); - if (DUK_UNLIKELY(extra < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; + if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) { + if (extra < 0) { + /* Clamping to zero makes the API more robust to calling code + * calculation errors. + */ + extra = 0; + } else { + /* Cause grow check to fail without wrapping arithmetic. */ + extra = DUK_USE_VALSTACK_LIMIT; + } } - min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA; - return duk_valstack_resize_raw(ctx, - min_new_size, /* min_new_size */ - 0 /* no shrink */ | /* flags */ - 0 /* no compact */ | - 0 /* no throw */); + min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) + + sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA); + return duk_valstack_grow_check_nothrow(thr, min_new_bytes); } -DUK_EXTERNAL void duk_require_stack(duk_context *ctx, duk_idx_t extra) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_size_t min_new_size; +DUK_EXTERNAL void duk_require_stack(duk_hthread *thr, duk_idx_t extra) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); - if (DUK_UNLIKELY(extra < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; + if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) { + if (extra < 0) { + /* Clamping to zero makes the API more robust to calling code + * calculation errors. + */ + extra = 0; + } else { + /* Cause grow check to fail without wrapping arithmetic. */ + extra = DUK_USE_VALSTACK_LIMIT; + } } - min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA; - (void) duk_valstack_resize_raw(ctx, - min_new_size, /* min_new_size */ - 0 /* no shrink */ | /* flags */ - 0 /* no compact */ | - DUK_VSRESIZE_FLAG_THROW); + min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) + + sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA); + duk_valstack_grow_check_throw(thr, min_new_bytes); } -DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top) { - duk_size_t min_new_size; +DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_hthread *thr, duk_idx_t top) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(top < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; + if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) { + if (top < 0) { + /* Clamping to zero makes the API more robust to calling code + * calculation errors. + */ + top = 0; + } else { + /* Cause grow check to fail without wrapping arithmetic. */ + top = DUK_USE_VALSTACK_LIMIT; + } } - min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA; - return duk_valstack_resize_raw(ctx, - min_new_size, /* min_new_size */ - 0 /* no shrink */ | /* flags */ - 0 /* no compact */ | - 0 /* no throw */); + DUK_ASSERT(top >= 0); + min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) + + sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA); + return duk_valstack_grow_check_nothrow(thr, min_new_bytes); } -DUK_EXTERNAL void duk_require_stack_top(duk_context *ctx, duk_idx_t top) { - duk_size_t min_new_size; +DUK_EXTERNAL void duk_require_stack_top(duk_hthread *thr, duk_idx_t top) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(top < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; + if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) { + if (top < 0) { + /* Clamping to zero makes the API more robust to calling code + * calculation errors. + */ + top = 0; + } else { + /* Cause grow check to fail without wrapping arithmetic. */ + top = DUK_USE_VALSTACK_LIMIT; + } } - min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA; - (void) duk_valstack_resize_raw(ctx, - min_new_size, /* min_new_size */ - 0 /* no shrink */ | /* flags */ - 0 /* no compact */ | - DUK_VSRESIZE_FLAG_THROW); + DUK_ASSERT(top >= 0); + min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) + + sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA); + duk_valstack_grow_check_throw(thr, min_new_bytes); } /* * Basic stack manipulation: swap, dup, insert, replace, etc */ -DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) { +DUK_EXTERNAL void duk_swap(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { duk_tval *tv1; duk_tval *tv2; duk_tval tv_tmp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_require_tval(ctx, index1); + tv1 = duk_require_tval(thr, idx1); DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(ctx, index2); + tv2 = duk_require_tval(thr, idx2); DUK_ASSERT(tv2 != NULL); /* If tv1==tv2 this is a NOP, no check is needed */ @@ -15755,22 +19054,20 @@ DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) DUK_TVAL_SET_TVAL(tv2, &tv_tmp); } -DUK_EXTERNAL void duk_swap_top(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_swap_top(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - duk_swap(ctx, index, -1); + duk_swap(thr, idx, -1); } -DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_index) { - duk_hthread *thr; +DUK_EXTERNAL void duk_dup(duk_hthread *thr, duk_idx_t from_idx) { duk_tval *tv_from; duk_tval *tv_to; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); - tv_from = duk_require_tval(ctx, from_index); + tv_from = duk_require_tval(thr, from_idx); tv_to = thr->valstack_top++; DUK_ASSERT(tv_from != NULL); DUK_ASSERT(tv_to != NULL); @@ -15778,18 +19075,19 @@ DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_index) { DUK_TVAL_INCREF(thr, tv_to); /* no side effects */ } -DUK_EXTERNAL void duk_dup_top(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_dup_top(duk_hthread *thr) { +#if defined(DUK_USE_PREFER_SIZE) + duk_dup(thr, -1); +#else duk_tval *tv_from; duk_tval *tv_to; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); - if (thr->valstack_top - thr->valstack_bottom <= 0) { - DUK_ERROR_API_INDEX(thr, -1); - return; /* unreachable */ + if (DUK_UNLIKELY(thr->valstack_top - thr->valstack_bottom <= 0)) { + DUK_ERROR_RANGE_INDEX(thr, -1); + DUK_WO_NORETURN(return;); } tv_from = thr->valstack_top - 1; tv_to = thr->valstack_top++; @@ -15797,19 +19095,45 @@ DUK_EXTERNAL void duk_dup_top(duk_context *ctx) { DUK_ASSERT(tv_to != NULL); DUK_TVAL_SET_TVAL(tv_to, tv_from); DUK_TVAL_INCREF(thr, tv_to); /* no side effects */ +#endif } -DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_index) { +DUK_INTERNAL void duk_dup_0(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, 0); +} +DUK_INTERNAL void duk_dup_1(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, 1); +} +DUK_INTERNAL void duk_dup_2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, 2); +} +DUK_INTERNAL void duk_dup_m2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, -2); +} +DUK_INTERNAL void duk_dup_m3(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, -3); +} +DUK_INTERNAL void duk_dup_m4(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, -4); +} + +DUK_EXTERNAL void duk_insert(duk_hthread *thr, duk_idx_t to_idx) { duk_tval *p; duk_tval *q; duk_tval tv_tmp; duk_size_t nbytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - p = duk_require_tval(ctx, to_index); + p = duk_require_tval(thr, to_idx); DUK_ASSERT(p != NULL); - q = duk_require_tval(ctx, -1); + q = duk_require_tval(thr, -1); DUK_ASSERT(q != NULL); DUK_ASSERT(q >= p); @@ -15820,40 +19144,90 @@ DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_index) { * => [ ... | q | p | x | x ] */ - nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */ + nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); - DUK_DDD(DUK_DDDPRINT("duk_insert: to_index=%ld, p=%p, q=%p, nbytes=%lu", - (long) to_index, (void *) p, (void *) q, (unsigned long) nbytes)); + DUK_DDD(DUK_DDDPRINT("duk_insert: to_idx=%ld, p=%p, q=%p, nbytes=%lu", + (long) to_idx, (void *) p, (void *) q, (unsigned long) nbytes)); - /* No net refcount changes. */ + /* No net refcount changes. No need to special case nbytes == 0 + * (p == q). + */ + DUK_TVAL_SET_TVAL(&tv_tmp, q); + duk_memmove((void *) (p + 1), (const void *) p, (size_t) nbytes); + DUK_TVAL_SET_TVAL(p, &tv_tmp); +} - if (nbytes > 0) { - DUK_TVAL_SET_TVAL(&tv_tmp, q); - DUK_ASSERT(nbytes > 0); - DUK_MEMMOVE((void *) (p + 1), (const void *) p, (size_t) nbytes); - DUK_TVAL_SET_TVAL(p, &tv_tmp); - } else { - /* nop: insert top to top */ - DUK_ASSERT(nbytes == 0); - DUK_ASSERT(p == q); +DUK_INTERNAL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(idx >= 0); /* Doesn't support negative indices. */ + + duk_push_undefined(thr); + duk_insert(thr, idx); +} + +DUK_INTERNAL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { + duk_tval *tv, *tv_end; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(idx >= 0); /* Doesn't support negative indices or count. */ + DUK_ASSERT(count >= 0); + + tv = duk_reserve_gap(thr, idx, count); + tv_end = tv + count; + while (tv != tv_end) { + DUK_TVAL_SET_UNDEFINED(tv); + tv++; } } -DUK_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_pull(duk_hthread *thr, duk_idx_t from_idx) { + duk_tval *p; + duk_tval *q; + duk_tval tv_tmp; + duk_size_t nbytes; + + DUK_ASSERT_API_ENTRY(thr); + + /* nbytes + * <---------> + * [ ... | x | x | p | y | y | q ] + * => [ ... | x | x | y | y | q | p ] + */ + + p = duk_require_tval(thr, from_idx); + DUK_ASSERT(p != NULL); + q = duk_require_tval(thr, -1); + DUK_ASSERT(q != NULL); + + DUK_ASSERT(q >= p); + + nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); + + DUK_DDD(DUK_DDDPRINT("duk_pull: from_idx=%ld, p=%p, q=%p, nbytes=%lu", + (long) from_idx, (void *) p, (void *) q, (unsigned long) nbytes)); + + /* No net refcount changes. No need to special case nbytes == 0 + * (p == q). + */ + DUK_TVAL_SET_TVAL(&tv_tmp, p); + duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes); + DUK_TVAL_SET_TVAL(q, &tv_tmp); +} + +DUK_EXTERNAL void duk_replace(duk_hthread *thr, duk_idx_t to_idx) { duk_tval *tv1; duk_tval *tv2; duk_tval tv_tmp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_require_tval(ctx, -1); + tv1 = duk_require_tval(thr, -1); DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(ctx, to_index); + tv2 = duk_require_tval(thr, to_idx); DUK_ASSERT(tv2 != NULL); /* For tv1 == tv2, both pointing to stack top, the end result - * is same as duk_pop(ctx). + * is same as duk_pop(thr). */ DUK_TVAL_SET_TVAL(&tv_tmp, tv2); DUK_TVAL_SET_TVAL(tv2, tv1); @@ -15862,37 +19236,34 @@ DUK_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_index) { DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ } -DUK_EXTERNAL void duk_copy(duk_context *ctx, duk_idx_t from_index, duk_idx_t to_index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_copy(duk_hthread *thr, duk_idx_t from_idx, duk_idx_t to_idx) { duk_tval *tv1; duk_tval *tv2; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); /* w/o refcounting */ + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_require_tval(ctx, from_index); + tv1 = duk_require_tval(thr, from_idx); DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(ctx, to_index); + tv2 = duk_require_tval(thr, to_idx); DUK_ASSERT(tv2 != NULL); /* For tv1 == tv2, this is a no-op (no explicit check needed). */ DUK_TVAL_SET_TVAL_UPDREF(thr, tv2, tv1); /* side effects */ } -DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_remove(duk_hthread *thr, duk_idx_t idx) { duk_tval *p; duk_tval *q; -#ifdef DUK_USE_REFERENCE_COUNTING +#if defined(DUK_USE_REFERENCE_COUNTING) duk_tval tv_tmp; #endif duk_size_t nbytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - p = duk_require_tval(ctx, index); + p = duk_require_tval(thr, idx); DUK_ASSERT(p != NULL); - q = duk_require_tval(ctx, -1); + q = duk_require_tval(thr, -1); DUK_ASSERT(q != NULL); DUK_ASSERT(q >= p); @@ -15903,29 +19274,90 @@ DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t index) { * => [ ... | x | x | q ] [ ... ] */ -#ifdef DUK_USE_REFERENCE_COUNTING +#if defined(DUK_USE_REFERENCE_COUNTING) /* use a temp: decref only when valstack reachable values are correct */ DUK_TVAL_SET_TVAL(&tv_tmp, p); #endif nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */ - DUK_MEMMOVE((void *) p, (const void *) (p + 1), (size_t) nbytes); /* zero size not an issue: pointers are valid */ + duk_memmove((void *) p, (const void *) (p + 1), (size_t) nbytes); DUK_TVAL_SET_UNDEFINED(q); thr->valstack_top--; -#ifdef DUK_USE_REFERENCE_COUNTING +#if defined(DUK_USE_REFERENCE_COUNTING) DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ #endif } +DUK_INTERNAL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + duk_remove(thr, idx); /* XXX: no optimization for now */ +} + +DUK_INTERNAL void duk_remove_m2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + + duk_remove(thr, -2); +} + +DUK_INTERNAL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { +#if defined(DUK_USE_PREFER_SIZE) + /* XXX: maybe too slow even when preferring size? */ + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT(idx >= 0); + + while (count-- > 0) { + duk_remove(thr, idx); + } +#else /* DUK_USE_PREFER_SIZE */ + duk_tval *tv_src; + duk_tval *tv_dst; + duk_tval *tv_newtop; + duk_tval *tv; + duk_size_t bytes; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT(idx >= 0); + + tv_dst = thr->valstack_bottom + idx; + DUK_ASSERT(tv_dst <= thr->valstack_top); + tv_src = tv_dst + count; + DUK_ASSERT(tv_src <= thr->valstack_top); + bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src); + + for (tv = tv_dst; tv < tv_src; tv++) { + DUK_TVAL_DECREF_NORZ(thr, tv); + } + + duk_memmove((void *) tv_dst, (const void *) tv_src, bytes); + + tv_newtop = thr->valstack_top - count; + for (tv = tv_newtop; tv < thr->valstack_top; tv++) { + DUK_TVAL_SET_UNDEFINED(tv); + } + thr->valstack_top = tv_newtop; + + /* When not preferring size, only NORZ macros are used; caller + * is expected to DUK_REFZERO_CHECK(). + */ +#endif /* DUK_USE_PREFER_SIZE */ +} + +DUK_INTERNAL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + + duk_remove_n(thr, idx, count); /* XXX: no optimization for now */ +} + /* * Stack slice primitives */ -DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy) { - duk_hthread *to_thr = (duk_hthread *) to_ctx; - duk_hthread *from_thr = (duk_hthread *) from_ctx; +DUK_EXTERNAL void duk_xcopymove_raw(duk_hthread *to_thr, duk_hthread *from_thr, duk_idx_t count, duk_bool_t is_copy) { void *src; duk_size_t nbytes; duk_tval *p; @@ -15933,40 +19365,44 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, /* XXX: several pointer comparison issues here */ - DUK_ASSERT_CTX_VALID(to_ctx); - DUK_ASSERT_CTX_VALID(from_ctx); - DUK_ASSERT(to_ctx != NULL); - DUK_ASSERT(from_ctx != NULL); + DUK_ASSERT_API_ENTRY(to_thr); + DUK_CTX_ASSERT_VALID(to_thr); + DUK_CTX_ASSERT_VALID(from_thr); + DUK_ASSERT(to_thr->heap == from_thr->heap); - if (to_ctx == from_ctx) { - DUK_ERROR_API(to_thr, DUK_STR_INVALID_CONTEXT); - return; + if (DUK_UNLIKELY(to_thr == from_thr)) { + DUK_ERROR_TYPE(to_thr, DUK_STR_INVALID_CONTEXT); + DUK_WO_NORETURN(return;); } - if ((count < 0) || - (count > (duk_idx_t) to_thr->valstack_max)) { - /* Maximum value check ensures 'nbytes' won't wrap below. */ - DUK_ERROR_API(to_thr, DUK_STR_INVALID_COUNT); - return; + if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) DUK_USE_VALSTACK_LIMIT)) { + /* Maximum value check ensures 'nbytes' won't wrap below. + * Also handles negative count. + */ + DUK_ERROR_RANGE_INVALID_COUNT(to_thr); + DUK_WO_NORETURN(return;); } + DUK_ASSERT(count >= 0); - nbytes = sizeof(duk_tval) * count; - if (nbytes == 0) { + nbytes = sizeof(duk_tval) * (duk_size_t) count; + if (DUK_UNLIKELY(nbytes == 0)) { return; } DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end); - if ((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes) { - DUK_ERROR_API(to_thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); + if (DUK_UNLIKELY((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes)) { + DUK_ERROR_RANGE_PUSH_BEYOND(to_thr); + DUK_WO_NORETURN(return;); } src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes); - if (src < (void *) from_thr->valstack_bottom) { - DUK_ERROR_API(to_thr, DUK_STR_INVALID_COUNT); + if (DUK_UNLIKELY(src < (void *) from_thr->valstack_bottom)) { + DUK_ERROR_RANGE_INVALID_COUNT(to_thr); + DUK_WO_NORETURN(return;); } - /* copy values (no overlap even if to_ctx == from_ctx; that's not - * allowed now anyway) + /* Copy values (no overlap even if to_thr == from_thr; that's not + * allowed now anyway). */ DUK_ASSERT(nbytes > 0); - DUK_MEMCPY((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes); + duk_memcpy((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes); p = to_thr->valstack_top; to_thr->valstack_top = (duk_tval *) (void *) (((duk_uint8_t *) p) + nbytes); @@ -15992,338 +19428,640 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, } } +/* Internal helper: reserve a gap of 'count' elements at 'idx_base' and return a + * pointer to the gap. Values in the gap are garbage and MUST be initialized by + * the caller before any side effects may occur. The caller must ensure there's + * enough stack reserve for 'count' values. + */ +DUK_INTERNAL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count) { + duk_tval *tv_src; + duk_tval *tv_dst; + duk_size_t gap_bytes; + duk_size_t copy_bytes; + + /* Caller is responsible for ensuring there's enough preallocated + * value stack. + */ + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack_top) >= (duk_size_t) count); + + tv_src = thr->valstack_bottom + idx_base; + gap_bytes = (duk_size_t) count * sizeof(duk_tval); + tv_dst = (duk_tval *) (void *) ((duk_uint8_t *) tv_src + gap_bytes); + copy_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src); + thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + gap_bytes); + duk_memmove((void *) tv_dst, (const void *) tv_src, copy_bytes); + + /* Values in the gap are left as garbage: caller must fill them in + * and INCREF them before any side effects. + */ + return tv_src; +} + /* - * Get/require + * Get/opt/require */ -DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_require_undefined(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_UNDEFINED(tv)) { - return; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_UNDEFINED(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "undefined", DUK_STR_NOT_UNDEFINED); + DUK_WO_NORETURN(return;); } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "undefined", DUK_STR_NOT_UNDEFINED); - return; /* not reachable */ } -DUK_EXTERNAL void duk_require_null(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_require_null(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_NULL(tv)) { - return; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_NULL(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "null", DUK_STR_NOT_NULL); + DUK_WO_NORETURN(return;); } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "null", DUK_STR_NOT_NULL); - return; /* not reachable */ } -DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t index) { - duk_bool_t ret = 0; /* default: false */ +DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { + duk_bool_t ret; duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BOOLEAN(tv)) { + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_BOOLEAN(tv)) { ret = DUK_TVAL_GET_BOOLEAN(tv); + DUK_ASSERT(ret == 0 || ret == 1); + } else { + ret = def_value; + /* Not guaranteed to be 0 or 1. */ } - DUK_ASSERT(ret == 0 || ret == 1); return ret; } -DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval *tv; +DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BOOLEAN(tv)) { - duk_bool_t ret = DUK_TVAL_GET_BOOLEAN(tv); - DUK_ASSERT(ret == 0 || ret == 1); - return ret; - } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "boolean", DUK_STR_NOT_BOOLEAN); - return 0; /* not reachable */ + return duk__get_boolean_raw(thr, idx, 0); /* default: false */ } -DUK_EXTERNAL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + return duk__get_boolean_raw(thr, idx, def_value); +} + +DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + duk_bool_t ret; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_LIKELY(DUK_TVAL_IS_BOOLEAN(tv))) { + ret = DUK_TVAL_GET_BOOLEAN(tv); + DUK_ASSERT(ret == 0 || ret == 1); + return ret; + } else { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "boolean", DUK_STR_NOT_BOOLEAN); + DUK_WO_NORETURN(return 0;); + } +} + +DUK_EXTERNAL duk_bool_t duk_opt_boolean(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_boolean(thr, idx); +} + +DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { duk_double_union ret; duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); - ret.d = DUK_DOUBLE_NAN; /* default: NaN */ - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_NUMBER(tv)) { - ret.d = DUK_TVAL_GET_NUMBER(tv); + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); +#if defined(DUK_USE_FASTINT) + if (DUK_TVAL_IS_FASTINT(tv)) { + ret.d = (duk_double_t) DUK_TVAL_GET_FASTINT(tv); /* XXX: cast trick */ + } + else +#endif + if (DUK_TVAL_IS_DOUBLE(tv)) { + /* When using packed duk_tval, number must be in NaN-normalized form + * for it to be a duk_tval, so no need to normalize. NOP for unpacked + * duk_tval. + */ + ret.d = DUK_TVAL_GET_DOUBLE(tv); + DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); + } else { + ret.d = def_value; + /* Default value (including NaN) may not be normalized. */ } - /* - * Number should already be in NaN-normalized form, but let's - * normalize anyway. - */ - - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret); return ret.d; } -DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_double_t duk_get_number(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_number_raw(thr, idx, DUK_DOUBLE_NAN); /* default: NaN */ +} + +DUK_EXTERNAL duk_double_t duk_get_number_default(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_number_raw(thr, idx, def_value); +} + +DUK_EXTERNAL duk_double_t duk_require_number(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; + duk_double_union ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_NUMBER(tv)) { - duk_double_union ret; - ret.d = DUK_TVAL_GET_NUMBER(tv); - - /* - * Number should already be in NaN-normalized form, - * but let's normalize anyway. - */ - - DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret); - return ret.d; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_NUMBER(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); + DUK_WO_NORETURN(return 0.0;); } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER); - return DUK_DOUBLE_NAN; /* not reachable */ + + ret.d = DUK_TVAL_GET_NUMBER(tv); + + /* When using packed duk_tval, number must be in NaN-normalized form + * for it to be a duk_tval, so no need to normalize. NOP for unpacked + * duk_tval. + */ + DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); + return ret.d; } -DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t index) { - /* Custom coercion for API */ - DUK_ASSERT_CTX_VALID(ctx); - return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/); +DUK_EXTERNAL duk_double_t duk_opt_number(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + /* User provided default is not NaN normalized. */ + return def_value; + } + return duk_require_number(thr, idx); } -DUK_EXTERNAL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t index) { - /* Custom coercion for API */ - DUK_ASSERT_CTX_VALID(ctx); - return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/); +DUK_EXTERNAL duk_int_t duk_get_int(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/); } -DUK_EXTERNAL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t index) { - /* Custom coercion for API */ - DUK_ASSERT_CTX_VALID(ctx); - return (duk_int_t) duk__api_coerce_d2i(ctx, index, 1 /*require*/); +DUK_EXTERNAL duk_uint_t duk_get_uint(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/); } -DUK_EXTERNAL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t index) { - /* Custom coercion for API */ - DUK_ASSERT_CTX_VALID(ctx); - return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 1 /*require*/); +DUK_EXTERNAL duk_int_t duk_get_int_default(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_int_t) duk__api_coerce_d2i(thr, idx, def_value, 0 /*require*/); } -DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) { +DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, def_value, 0 /*require*/); +} + +DUK_EXTERNAL duk_int_t duk_require_int(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 1 /*require*/); +} + +DUK_EXTERNAL duk_uint_t duk_require_uint(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 1 /*require*/); +} + +DUK_EXTERNAL duk_int_t duk_opt_int(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_int(thr, idx); +} + +DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_uint(thr, idx); +} + +DUK_EXTERNAL const char *duk_get_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + duk_hstring *h; const char *ret; - duk_tval *tv; + duk_size_t len; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* default: NULL, length 0 */ - ret = NULL; - if (out_len) { - *out_len = 0; - } - - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_STRING(tv)) { - /* Here we rely on duk_hstring instances always being zero - * terminated even if the actual string is not. - */ - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); + h = duk_get_hstring(thr, idx); + if (h != NULL) { + len = DUK_HSTRING_GET_BYTELEN(h); ret = (const char *) DUK_HSTRING_GET_DATA(h); - if (out_len) { - *out_len = DUK_HSTRING_GET_BYTELEN(h); - } + } else { + len = 0; + ret = NULL; } + if (out_len != NULL) { + *out_len = len; + } return ret; } -DUK_EXTERNAL const char *duk_require_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_require_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_require_hstring(thr, idx); + DUK_ASSERT(h != NULL); + if (out_len) { + *out_len = DUK_HSTRING_GET_BYTELEN(h); + } + return (const char *) DUK_HSTRING_GET_DATA(h); +} + +DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_require_hstring_notsymbol(thr, idx); + DUK_ASSERT(h != NULL); + if (out_len) { + *out_len = DUK_HSTRING_GET_BYTELEN(h); + } + return (const char *) DUK_HSTRING_GET_DATA(h); +} + +DUK_EXTERNAL const char *duk_get_string(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_get_hstring(thr, idx); + if (h != NULL) { + return (const char *) DUK_HSTRING_GET_DATA(h); + } else { + return NULL; + } +} + +DUK_EXTERNAL const char *duk_opt_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_len != NULL) { + *out_len = def_len; + } + return def_ptr; + } + return duk_require_lstring(thr, idx, out_len); +} + +DUK_EXTERNAL const char *duk_opt_string(duk_hthread *thr, duk_idx_t idx, const char *def_ptr) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_ptr; + } + return duk_require_string(thr, idx); +} + +DUK_EXTERNAL const char *duk_get_lstring_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) { + duk_hstring *h; const char *ret; + duk_size_t len; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* Note: this check relies on the fact that even a zero-size string - * has a non-NULL pointer. - */ - ret = duk_get_lstring(ctx, index, out_len); - if (ret) { - return ret; - } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "string", DUK_STR_NOT_STRING); - return NULL; /* not reachable */ -} - -DUK_EXTERNAL const char *duk_get_string(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - - return duk_get_lstring(ctx, index, NULL); -} - -DUK_EXTERNAL const char *duk_require_string(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - - return duk_require_lstring(ctx, index, NULL); -} - -DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; - - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_POINTER(tv)) { - void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ - return (void *) p; + h = duk_get_hstring(thr, idx); + if (h != NULL) { + len = DUK_HSTRING_GET_BYTELEN(h); + ret = (const char *) DUK_HSTRING_GET_DATA(h); + } else { + len = def_len; + ret = def_ptr; } - return NULL; + if (out_len != NULL) { + *out_len = len; + } + return ret; } -DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_get_string_default(duk_hthread *thr, duk_idx_t idx, const char *def_value) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_get_hstring(thr, idx); + if (h != NULL) { + return (const char *) DUK_HSTRING_GET_DATA(h); + } else { + return def_value; + } +} + +DUK_INTERNAL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_get_hstring_notsymbol(thr, idx); + if (h) { + return (const char *) DUK_HSTRING_GET_DATA(h); + } else { + return NULL; + } +} + +DUK_EXTERNAL const char *duk_require_string(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return duk_require_lstring(thr, idx, NULL); +} + +DUK_INTERNAL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = duk_require_hstring_notsymbol(thr, idx); + DUK_ASSERT(h != NULL); + return (const char *) DUK_HSTRING_GET_DATA(h); +} + +DUK_EXTERNAL void duk_require_object(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); + DUK_WO_NORETURN(return;); + } +} + +DUK_LOCAL void *duk__get_pointer_raw(duk_hthread *thr, duk_idx_t idx, void *def_value) { + duk_tval *tv; + void *p; + + DUK_CTX_ASSERT_VALID(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (!DUK_TVAL_IS_POINTER(tv)) { + return def_value; + } + + p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ + return p; +} + +DUK_EXTERNAL void *duk_get_pointer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_pointer_raw(thr, idx, NULL /*def_value*/); +} + +DUK_EXTERNAL void *duk_opt_pointer(duk_hthread *thr, duk_idx_t idx, void *def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_pointer(thr, idx); +} + +DUK_EXTERNAL void *duk_get_pointer_default(duk_hthread *thr, duk_idx_t idx, void *def_value) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_pointer_raw(thr, idx, def_value); +} + +DUK_EXTERNAL void *duk_require_pointer(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + void *p; + + DUK_ASSERT_API_ENTRY(thr); /* Note: here we must be wary of the fact that a pointer may be * valid and be a NULL. */ - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_POINTER(tv)) { - void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ - return (void *) p; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_POINTER(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "pointer", DUK_STR_NOT_POINTER); + DUK_WO_NORETURN(return NULL;); } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "pointer", DUK_STR_NOT_POINTER); - return NULL; /* not reachable */ + p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ + return p; } #if 0 /*unused*/ -DUK_INTERNAL void *duk_get_voidptr(duk_context *ctx, duk_idx_t index) { +DUK_INTERNAL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; + duk_heaphdr *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(h != NULL); - return (void *) h; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + return NULL; } - return NULL; + h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + return (void *) h; } #endif -DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_bool_t throw_flag) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL void *duk__get_buffer_helper(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag) { + duk_hbuffer *h; + void *ret; + duk_size_t len; duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_CTX_ASSERT_VALID(thr); if (out_size != NULL) { *out_size = 0; } - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_LIKELY(DUK_TVAL_IS_BUFFER(tv))) { + h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); - if (out_size) { - *out_size = DUK_HBUFFER_GET_SIZE(h); + + len = DUK_HBUFFER_GET_SIZE(h); + ret = DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); + } else { + if (throw_flag) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); + DUK_WO_NORETURN(return NULL;); } - return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */ + len = def_size; + ret = def_ptr; } - if (throw_flag) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "buffer", DUK_STR_NOT_BUFFER); - } - return NULL; -} - -DUK_EXTERNAL void *duk_get_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { - return duk__get_buffer_helper(ctx, index, out_size, 0 /*throw_flag*/); -} - -DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { - return duk__get_buffer_helper(ctx, index, out_size, 1 /*throw_flag*/); -} - -DUK_LOCAL void *duk__get_buffer_data_helper(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_bool_t throw_flag) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval *tv; - - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); - if (out_size != NULL) { - *out_size = 0; + *out_size = len; + } + return ret; +} + +DUK_EXTERNAL void *duk_get_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { + DUK_ASSERT_API_ENTRY(thr); + + return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/); +} + +DUK_EXTERNAL void *duk_opt_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_size != NULL) { + *out_size = def_size; + } + return def_ptr; + } + return duk_require_buffer(thr, idx, out_size); +} + +DUK_EXTERNAL void *duk_get_buffer_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len) { + DUK_ASSERT_API_ENTRY(thr); + + return duk__get_buffer_helper(thr, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/); +} + +DUK_EXTERNAL void *duk_require_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { + DUK_ASSERT_API_ENTRY(thr); + + return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/); +} + +/* Get the active buffer data area for a plain buffer or a buffer object. + * Return NULL if the the value is not a buffer. Note that a buffer may + * have a NULL data pointer when its size is zero, the optional 'out_isbuffer' + * argument allows caller to detect this reliably. + */ +DUK_INTERNAL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + if (out_isbuffer != NULL) { + *out_isbuffer = 0; + } + if (out_size != NULL) { + *out_size = def_size; } - tv = duk_get_tval(ctx, index); - if (tv == NULL) { - goto fail; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_BUFFER(tv)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); - if (out_size) { + if (out_size != NULL) { *out_size = DUK_HBUFFER_GET_SIZE(h); } + if (out_isbuffer != NULL) { + *out_isbuffer = 1; + } return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */ - } else if (DUK_TVAL_IS_OBJECT(tv)) { + } +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) + else if (DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) { + if (DUK_HOBJECT_IS_BUFOBJ(h)) { /* XXX: this is probably a useful shared helper: for a - * duk_hbufferobject, get a validated buffer pointer/length. + * duk_hbufobj, get a validated buffer pointer/length. */ - duk_hbufferobject *h_bufobj = (duk_hbufferobject *) h; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + duk_hbufobj *h_bufobj = (duk_hbufobj *) h; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); if (h_bufobj->buf != NULL && - DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) { + DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { duk_uint8_t *p; p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf); if (out_size != NULL) { *out_size = (duk_size_t) h_bufobj->length; } + if (out_isbuffer != NULL) { + *out_isbuffer = 1; + } return (void *) (p + h_bufobj->offset); } /* if slice not fully valid, treat as error */ } } +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - fail: if (throw_flag) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "buffer", DUK_STR_NOT_BUFFER); + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); + DUK_WO_NORETURN(return NULL;); } - return NULL; + return def_ptr; } -DUK_EXTERNAL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { - return duk__get_buffer_data_helper(ctx, index, out_size, 0 /*throw_flag*/); +DUK_EXTERNAL void *duk_get_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { + DUK_ASSERT_API_ENTRY(thr); + return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL); } -DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) { - return duk__get_buffer_data_helper(ctx, index, out_size, 1 /*throw_flag*/); +DUK_EXTERNAL void *duk_get_buffer_data_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + DUK_ASSERT_API_ENTRY(thr); + return duk_get_buffer_data_raw(thr, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL); +} + +DUK_EXTERNAL void *duk_opt_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_size != NULL) { + *out_size = def_size; + } + return def_ptr; + } + return duk_require_buffer_data(thr, idx, out_size); +} + +DUK_EXTERNAL void *duk_require_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) { + DUK_ASSERT_API_ENTRY(thr); + return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/, NULL); } /* Raw helper for getting a value from the stack, checking its tag. @@ -16331,318 +20069,448 @@ DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t index, du * tag in the packed representation. */ -DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t index, duk_uint_t tag) { +DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag) { duk_tval *tv; + duk_heaphdr *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); - tv = duk_get_tval(ctx, index); - if (tv && (DUK_TVAL_GET_TAG(tv) == tag)) { - duk_heaphdr *ret; - ret = DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); /* tagged null pointers should never occur */ - return ret; + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_GET_TAG(tv) != tag) { + return (duk_heaphdr *) NULL; } - return (duk_heaphdr *) NULL; + ret = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(ret != NULL); /* tagged null pointers should never occur */ + return ret; + } -DUK_INTERNAL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t index) { - return (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING); +DUK_INTERNAL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); } -DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t index) { - duk_heaphdr *h; - h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING); - if (h == NULL) { - DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "string", DUK_STR_NOT_STRING); - } - return (duk_hstring *) h; -} +DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; -DUK_INTERNAL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t index) { - return (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); -} + DUK_ASSERT_API_ENTRY(thr); -DUK_INTERNAL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t index) { - duk_heaphdr *h; - h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (h == NULL) { - DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "object", DUK_STR_NOT_OBJECT); - } - return (duk_hobject *) h; -} - -DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t index) { - return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER); -} - -DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t index) { - duk_heaphdr *h; - h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER); - if (h == NULL) { - DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "buffer", DUK_STR_NOT_BUFFER); - } - return (duk_hbuffer *) h; -} - -DUK_INTERNAL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t index) { - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (h != NULL && !DUK_HOBJECT_IS_THREAD(h)) { - h = NULL; - } - return (duk_hthread *) h; -} - -DUK_INTERNAL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (!(h != NULL && DUK_HOBJECT_IS_THREAD(h))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "thread", DUK_STR_NOT_THREAD); - } - return (duk_hthread *) h; -} - -DUK_INTERNAL duk_hcompiledfunction *duk_get_hcompiledfunction(duk_context *ctx, duk_idx_t index) { - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (h != NULL && !DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { - h = NULL; - } - return (duk_hcompiledfunction *) h; -} - -DUK_INTERNAL duk_hcompiledfunction *duk_require_hcompiledfunction(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (!(h != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(h))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "compiledfunction", DUK_STR_NOT_COMPILEDFUNCTION); - } - return (duk_hcompiledfunction *) h; -} - -DUK_INTERNAL duk_hnativefunction *duk_get_hnativefunction(duk_context *ctx, duk_idx_t index) { - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (h != NULL && !DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { - h = NULL; - } - return (duk_hnativefunction *) h; -} - -DUK_INTERNAL duk_hnativefunction *duk_require_hnativefunction(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (!(h != NULL && DUK_HOBJECT_IS_NATIVEFUNCTION(h))) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION); - } - return (duk_hnativefunction *) h; -} - -DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; - duk_hobject *h; - duk_hnativefunction *f; - - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_get_tval(ctx, index); - if (!tv) { + h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); + if (DUK_UNLIKELY(h && DUK_HSTRING_HAS_SYMBOL(h))) { return NULL; } - if (!DUK_TVAL_IS_OBJECT(tv)) { + return h; +} + +DUK_INTERNAL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); + if (DUK_UNLIKELY(h == NULL)) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING); + DUK_WO_NORETURN(return NULL;); + } + return h; +} + +DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING); + if (DUK_UNLIKELY(h == NULL || DUK_HSTRING_HAS_SYMBOL(h))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING); + DUK_WO_NORETURN(return NULL;); + } + return h; +} + +DUK_INTERNAL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); +} + +DUK_INTERNAL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(h == NULL)) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); + DUK_WO_NORETURN(return NULL;); + } + return h; +} + +DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER); +} + +DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx) { + duk_hbuffer *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER); + if (DUK_UNLIKELY(h == NULL)) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); + DUK_WO_NORETURN(return NULL;); + } + return h; +} + +DUK_INTERNAL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_THREAD(h))) { + h = NULL; + } + return (duk_hthread *) h; +} + +DUK_INTERNAL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_THREAD(h)))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "thread", DUK_STR_NOT_THREAD); + DUK_WO_NORETURN(return NULL;); + } + return (duk_hthread *) h; +} + +DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h))) { + h = NULL; + } + return (duk_hcompfunc *) h; +} + +DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_COMPFUNC(h)))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "compiledfunction", DUK_STR_NOT_COMPFUNC); + DUK_WO_NORETURN(return NULL;); + } + return (duk_hcompfunc *) h; +} + +DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_NATFUNC(h))) { + h = NULL; + } + return (duk_hnatfunc *) h; +} + +DUK_INTERNAL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; + + DUK_ASSERT_API_ENTRY(thr); + + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_NATFUNC(h)))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC); + DUK_WO_NORETURN(return NULL;); + } + return (duk_hnatfunc *) h; +} + +DUK_EXTERNAL duk_c_function duk_get_c_function(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + duk_hobject *h; + duk_hnatfunc *f; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) { return NULL; } h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { + if (DUK_UNLIKELY(!DUK_HOBJECT_IS_NATFUNC(h))) { return NULL; } - DUK_ASSERT(DUK_HOBJECT_HAS_NATIVEFUNCTION(h)); - f = (duk_hnativefunction *) h; + DUK_ASSERT(DUK_HOBJECT_HAS_NATFUNC(h)); + f = (duk_hnatfunc *) h; return f->func; } -DUK_EXTERNAL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_c_function duk_opt_c_function(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_c_function(thr, idx); +} + +DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) { duk_c_function ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_get_c_function(ctx, index); - if (!ret) { - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION); + ret = duk_get_c_function(thr, idx); + if (ret != NULL) { + return ret; + } + + return def_value; +} + +DUK_EXTERNAL duk_c_function duk_require_c_function(duk_hthread *thr, duk_idx_t idx) { + duk_c_function ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = duk_get_c_function(thr, idx); + if (DUK_UNLIKELY(!ret)) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC); + DUK_WO_NORETURN(return ret;); } return ret; } -DUK_EXTERNAL void duk_require_function(duk_context *ctx, duk_idx_t index) { - if (!duk_is_function(ctx, index)) { - DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, index, "function", DUK_STR_NOT_FUNCTION); +DUK_EXTERNAL void duk_require_function(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + if (DUK_UNLIKELY(!duk_is_function(thr, idx))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "function", DUK_STR_NOT_FUNCTION); + DUK_WO_NORETURN(return;); } } -DUK_EXTERNAL duk_context *duk_get_context(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_require_constructable(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *h; - return (duk_context *) duk_get_hthread(ctx, index); + DUK_ASSERT_API_ENTRY(thr); + + h = duk_require_hobject_accept_mask(thr, idx, DUK_TYPE_MASK_LIGHTFUNC); + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE); + DUK_WO_NORETURN(return;); + } + /* Lightfuncs (h == NULL) are constructable. */ } -DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_hthread *duk_get_context(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - return (duk_context *) duk_require_hthread(ctx, index); + return duk_get_hthread(thr, idx); } -DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; - void *ret; +DUK_EXTERNAL duk_hthread *duk_require_context(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); + return duk_require_hthread(thr, idx); +} - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); +DUK_EXTERNAL duk_hthread *duk_opt_context(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_context(thr, idx); +} + +DUK_EXTERNAL duk_hthread *duk_get_context_default(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) { + duk_hthread *ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = duk_get_context(thr, idx); + if (ret != NULL) { return ret; } - return (void *) NULL; + return def_value; } -DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void *duk_get_heapptr(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; void *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_get_tval_or_unused(thr, idx); DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); - DUK_ASSERT(ret != NULL); + if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { + return (void *) NULL; + } + + ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(ret != NULL); + return ret; +} + +DUK_EXTERNAL void *duk_opt_heapptr(duk_hthread *thr, duk_idx_t idx, void *def_value) { + DUK_ASSERT_API_ENTRY(thr); + + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_heapptr(thr, idx); +} + +DUK_EXTERNAL void *duk_get_heapptr_default(duk_hthread *thr, duk_idx_t idx, void *def_value) { + void *ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = duk_get_heapptr(thr, idx); + if (ret != NULL) { return ret; } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "heapobject", DUK_STR_UNEXPECTED_TYPE); - return (void *) NULL; /* not reachable */ + return def_value; } -#if 0 -/* This would be pointless: we'd return NULL for both lightfuncs and - * unexpected types. - */ -DUK_INTERNAL duk_hobject *duk_get_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) { -} -#endif - -/* Useful for internal call sites where we either expect an object (function) - * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced - * to an object). Return value is NULL if value is neither an object nor a - * lightfunc. - */ -DUK_INTERNAL duk_hobject *duk_get_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL void *duk_require_heapptr(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; + void *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_get_tval_or_unused(thr, idx); DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_OBJECT(tv)) { - return DUK_TVAL_GET_OBJECT(tv); - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_to_object(ctx, index); - return duk_require_hobject(ctx, index); + if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE); + DUK_WO_NORETURN(return NULL;); } + ret = (void *) DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(ret != NULL); + return ret; +} + +/* Internal helper for getting/requiring a duk_hobject with possible promotion. */ +DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { + duk_uint_t val_mask; + duk_hobject *res; + + DUK_CTX_ASSERT_VALID(thr); + + res = duk_get_hobject(thr, idx); /* common case, not promoted */ + if (DUK_LIKELY(res != NULL)) { + DUK_ASSERT(res != NULL); + return res; + } + + val_mask = duk_get_type_mask(thr, idx); + if (val_mask & type_mask) { + if (type_mask & DUK_TYPE_MASK_PROMOTE) { + res = duk_to_hobject(thr, idx); + DUK_ASSERT(res != NULL); + return res; + } else { + return NULL; /* accept without promoting */ + } + } + + if (type_mask & DUK_TYPE_MASK_THROW) { + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT); + DUK_WO_NORETURN(return NULL;); + } return NULL; } -/* Useful for internal call sites where we either expect an object (function) - * or a lightfunc. Returns NULL for a lightfunc. +/* Get a duk_hobject * at 'idx'; if the value is not an object but matches the + * supplied 'type_mask', promote it to an object and return the duk_hobject *. + * This is useful for call sites which want an object but also accept a plain + * buffer and/or a lightfunc which gets automatically promoted to an object. + * Return value is NULL if value is neither an object nor a plain type allowed + * by the mask. */ -DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval *tv; - - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_require_tval(ctx, index); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_OBJECT(tv)) { - return DUK_TVAL_GET_OBJECT(tv); - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - return NULL; - } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "object", DUK_STR_NOT_OBJECT); - return NULL; /* not reachable */ +DUK_INTERNAL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_PROMOTE); } -/* Useful for internal call sites where we either expect an object (function) - * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced - * to an object). Return value is never NULL. +/* Like duk_get_hobject_promote_mask() but throw a TypeError instead of + * returning a NULL. */ -DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval *tv; - - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_require_tval(ctx, index); - if (DUK_TVAL_IS_OBJECT(tv)) { - return DUK_TVAL_GET_OBJECT(tv); - } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_to_object(ctx, index); - return duk_require_hobject(ctx, index); - } - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "object", DUK_STR_NOT_OBJECT); - return NULL; /* not reachable */ +DUK_INTERNAL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW | DUK_TYPE_MASK_PROMOTE); } -DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum) { +/* Require a duk_hobject * at 'idx'; if the value is not an object but matches the + * supplied 'type_mask', return a NULL instead. Otherwise throw a TypeError. + */ +DUK_INTERNAL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) { + DUK_ASSERT_API_ENTRY(thr); + return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW); +} + +DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) { duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */ DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX); - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum) { + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum)) { h = NULL; } return h; } -DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum) { - duk_hthread *thr; +DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) { duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */ DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX); - thr = (duk_hthread *) ctx; - h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT); - if (!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum)) { + h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT); + if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum))) { duk_hstring *h_class; h_class = DUK_HTHREAD_GET_STRING(thr, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum)); DUK_UNREF(h_class); - DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, (const char *) DUK_HSTRING_GET_DATA(h_class), DUK_STR_UNEXPECTED_TYPE); + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, (const char *) DUK_HSTRING_GET_DATA(h_class), DUK_STR_UNEXPECTED_TYPE); + DUK_WO_NORETURN(return NULL;); } return h; } -DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_size_t duk_get_length(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (!tv) { - return 0; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: @@ -16650,51 +20518,130 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) { case DUK_TAG_BOOLEAN: case DUK_TAG_POINTER: return 0; +#if defined(DUK_USE_PREFER_SIZE) + /* String and buffer have a virtual non-configurable .length property + * which is within size_t range so it can be looked up without specific + * type checks. Lightfuncs inherit from %NativeFunctionPrototype% + * which provides an inherited .length accessor; it could be overwritten + * to produce unexpected types or values, but just number convert and + * duk_size_t cast for now. + */ + case DUK_TAG_STRING: + case DUK_TAG_BUFFER: + case DUK_TAG_LIGHTFUNC: { + duk_size_t ret; + duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); + ret = (duk_size_t) duk_to_number_m1(thr); + duk_pop_unsafe(thr); + return ret; + } +#else /* DUK_USE_PREFER_SIZE */ case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + return 0; + } return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h); } - case DUK_TAG_OBJECT: { - duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h); - } case DUK_TAG_BUFFER: { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (duk_size_t) DUK_HBUFFER_GET_SIZE(h); } case DUK_TAG_LIGHTFUNC: { - duk_small_uint_t lf_flags; - lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); - return (duk_size_t) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); + /* We could look up the length from the lightfunc duk_tval, + * but since Duktape 2.2 lightfunc .length comes from + * %NativeFunctionPrototype% which can be overridden, so + * look up the property explicitly. + */ + duk_size_t ret; + duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); + ret = (duk_size_t) duk_to_number_m1(thr); + duk_pop_unsafe(thr); + return ret; + } +#endif /* DUK_USE_PREFER_SIZE */ + case DUK_TAG_OBJECT: { + duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + return (duk_size_t) duk_hobject_get_length(thr, h); } #if defined(DUK_USE_FASTINT) case DUK_TAG_FASTINT: #endif default: - /* number */ - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); + /* number or 'unused' */ + DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv) || DUK_TVAL_IS_UNUSED(tv)); return 0; } DUK_UNREACHABLE(); } -DUK_INTERNAL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t length) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h; +/* + * duk_known_xxx() helpers + * + * Used internally when we're 100% sure that a certain index is valid and + * contains an object of a certain type. For example, if we duk_push_object() + * we can then safely duk_known_hobject(thr, -1). These helpers just assert + * for the index and type, and if the assumptions are not valid, memory unsafe + * behavior happens. + */ - DUK_ASSERT_CTX_VALID(ctx); +DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + duk_heaphdr *h; - h = duk_get_hobject(ctx, index); - if (!h) { - return; + DUK_CTX_ASSERT_VALID(thr); + if (idx < 0) { + tv = thr->valstack_top + idx; + } else { + tv = thr->valstack_bottom + idx; } + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_ASSERT(tv < thr->valstack_top); + h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + return h; +} - duk_hobject_set_length(thr, h, (duk_uint32_t) length); /* XXX: typing */ +DUK_INTERNAL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_hstring(thr, idx) != NULL); + return (duk_hstring *) duk__known_heaphdr(thr, idx); +} + +DUK_INTERNAL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_hobject(thr, idx) != NULL); + return (duk_hobject *) duk__known_heaphdr(thr, idx); +} + +DUK_INTERNAL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_hbuffer(thr, idx) != NULL); + return (duk_hbuffer *) duk__known_heaphdr(thr, idx); +} + +DUK_INTERNAL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_hcompfunc(thr, idx) != NULL); + return (duk_hcompfunc *) duk__known_heaphdr(thr, idx); +} + +DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_hnatfunc(thr, idx) != NULL); + return (duk_hnatfunc *) duk__known_heaphdr(thr, idx); +} + +DUK_EXTERNAL void duk_set_length(duk_hthread *thr, duk_idx_t idx, duk_size_t len) { + DUK_ASSERT_API_ENTRY(thr); + + idx = duk_normalize_index(thr, idx); + duk_push_uint(thr, (duk_uint_t) len); + duk_put_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); } /* @@ -16707,249 +20654,352 @@ DUK_INTERNAL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t l /* E5 Section 8.12.8 */ -DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_context *ctx, duk_idx_t index, duk_small_int_t func_stridx) { - if (duk_get_prop_stridx(ctx, index, func_stridx)) { +DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t func_stridx) { + if (duk_get_prop_stridx(thr, idx, func_stridx)) { /* [ ... func ] */ - if (duk_is_callable(ctx, -1)) { - duk_dup(ctx, index); /* -> [ ... func this ] */ - duk_call_method(ctx, 0); /* -> [ ... retval ] */ - if (duk_is_primitive(ctx, -1)) { - duk_replace(ctx, index); + if (duk_is_callable(thr, -1)) { + duk_dup(thr, idx); /* -> [ ... func this ] */ + duk_call_method(thr, 0); /* -> [ ... retval ] */ + if (duk_is_primitive(thr, -1)) { + duk_replace(thr, idx); return 1; } /* [ ... retval ]; popped below */ } } - duk_pop(ctx); /* [ ... func/retval ] -> [ ... ] */ + duk_pop_unsafe(thr); /* [ ... func/retval ] -> [ ... ] */ return 0; } -DUK_EXTERNAL void duk_to_defaultvalue(duk_context *ctx, duk_idx_t index, duk_int_t hint) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *obj; - /* inline initializer for coercers[] is not allowed by old compilers like BCC */ - duk_small_int_t coercers[2]; - - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - - coercers[0] = DUK_STRIDX_VALUE_OF; - coercers[1] = DUK_STRIDX_TO_STRING; - - index = duk_require_normalize_index(ctx, index); - obj = duk_require_hobject_or_lfunc(ctx, index); - - if (hint == DUK_HINT_NONE) { - if (obj != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_DATE) { - hint = DUK_HINT_STRING; - } else { - hint = DUK_HINT_NUMBER; - } - } - - if (hint == DUK_HINT_STRING) { - coercers[0] = DUK_STRIDX_TO_STRING; - coercers[1] = DUK_STRIDX_VALUE_OF; - } - - if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[0])) { - return; - } - - if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[1])) { - return; - } - - DUK_ERROR_TYPE(thr, DUK_STR_DEFAULTVALUE_COERCE_FAILED); -} - -DUK_EXTERNAL void duk_to_undefined(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_to_undefined(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ } -DUK_EXTERNAL void duk_to_null(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_to_null(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); DUK_TVAL_SET_NULL_UPDREF(thr, tv); /* side effects */ } /* E5 Section 9.1 */ -DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t index, duk_int_t hint) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_LOCAL const char * const duk__toprim_hint_strings[3] = { + "default", "string", "number" +}; +DUK_LOCAL void duk__to_primitive_helper(duk_hthread *thr, duk_idx_t idx, duk_int_t hint, duk_bool_t check_symbol) { + /* Inline initializer for coercers[] is not allowed by old compilers like BCC. */ + duk_small_uint_t coercers[2]; + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING); - index = duk_require_normalize_index(ctx, index); + idx = duk_require_normalize_index(thr, idx); - if (!duk_check_type_mask(ctx, index, DUK_TYPE_MASK_OBJECT | - DUK_TYPE_MASK_LIGHTFUNC)) { - /* everything except object stay as is */ + /* If already primitive, return as is. */ + if (!duk_check_type_mask(thr, idx, DUK_TYPE_MASK_OBJECT | + DUK_TYPE_MASK_LIGHTFUNC | + DUK_TYPE_MASK_BUFFER)) { + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ return; } - duk_to_defaultvalue(ctx, index, hint); + + /* @@toPrimitive lookup. Also do for plain buffers and lightfuncs + * which mimic objects. + */ + if (check_symbol && duk_get_method_stridx(thr, idx, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_PRIMITIVE)) { + DUK_ASSERT(hint >= 0 && (duk_size_t) hint < sizeof(duk__toprim_hint_strings) / sizeof(const char *)); + duk_dup(thr, idx); + duk_push_string(thr, duk__toprim_hint_strings[hint]); + duk_call_method(thr, 1); /* [ ... method value hint ] -> [ ... res] */ + if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | + DUK_TYPE_MASK_LIGHTFUNC | + DUK_TYPE_MASK_BUFFER)) { + goto fail; + } + duk_replace(thr, idx); + return; + } + + /* Objects are coerced based on E5 specification. + * Lightfuncs are coerced because they behave like + * objects even if they're internally a primitive + * type. Same applies to plain buffers, which behave + * like ArrayBuffer objects since Duktape 2.x. + */ + + /* Hint magic for Date is unnecessary in ES2015 because of + * Date.prototype[@@toPrimitive]. However, it is needed if + * symbol support is not enabled. + */ +#if defined(DUK_USE_SYMBOL_BUILTIN) + if (hint == DUK_HINT_NONE) { + hint = DUK_HINT_NUMBER; + } +#else /* DUK_USE_SYMBOL_BUILTIN */ + if (hint == DUK_HINT_NONE) { + duk_small_uint_t class_number; + + class_number = duk_get_class_number(thr, idx); + if (class_number == DUK_HOBJECT_CLASS_DATE) { + hint = DUK_HINT_STRING; + } else { + hint = DUK_HINT_NUMBER; + } + } +#endif /* DUK_USE_SYMBOL_BUILTIN */ + + coercers[0] = DUK_STRIDX_VALUE_OF; + coercers[1] = DUK_STRIDX_TO_STRING; + if (hint == DUK_HINT_STRING) { + coercers[0] = DUK_STRIDX_TO_STRING; + coercers[1] = DUK_STRIDX_VALUE_OF; + } + + if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[0])) { + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ + return; + } + + if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[1])) { + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ + return; + } + + fail: + DUK_ERROR_TYPE(thr, DUK_STR_TOPRIMITIVE_FAILED); + DUK_WO_NORETURN(return;); } +DUK_EXTERNAL void duk_to_primitive(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) { + duk__to_primitive_helper(thr, idx, hint, 1 /*check_symbol*/); +} + +#if defined(DUK_USE_SYMBOL_BUILTIN) +DUK_INTERNAL void duk_to_primitive_ordinary(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) { + duk__to_primitive_helper(thr, idx, hint, 0 /*check_symbol*/); +} +#endif + /* E5 Section 9.2 */ -DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_bool_t val; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - - tv = duk_require_tval(ctx, index); + idx = duk_require_normalize_index(thr, idx); + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_ASSERT(tv != NULL); val = duk_js_toboolean(tv); DUK_ASSERT(val == 0 || val == 1); - /* Note: no need to re-lookup tv, conversion is side effect free */ + /* Note: no need to re-lookup tv, conversion is side effect free. */ DUK_ASSERT(tv != NULL); DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, val); /* side effects */ return val; } -DUK_EXTERNAL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr) { + duk_tval *tv; + duk_bool_t val; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_require_tval(thr, -1); + DUK_ASSERT(tv != NULL); + + val = duk_js_toboolean(tv); + DUK_ASSERT(val == 0 || val == 1); + + duk_pop_unsafe(thr); + return val; +} + +DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_double_t d; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + /* XXX: No need to normalize; the whole operation could be inlined here to + * avoid 'tv' re-lookup. + */ + idx = duk_require_normalize_index(thr, idx); + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_ASSERT(tv != NULL); - /* XXX: fastint? */ - d = duk_js_tonumber(thr, tv); + d = duk_js_tonumber(thr, tv); /* XXX: fastint coercion? now result will always be a non-fastint */ - /* Note: need to re-lookup because ToNumber() may have side effects */ - tv = duk_require_tval(ctx, index); + /* ToNumber() may have side effects so must relookup 'tv'. */ + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */ return d; } +DUK_INTERNAL duk_double_t duk_to_number_m1(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + return duk_to_number(thr, -1); +} +DUK_INTERNAL duk_double_t duk_to_number_m2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + return duk_to_number(thr, -2); +} + +DUK_INTERNAL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv) { +#if defined(DUK_USE_PREFER_SIZE) + duk_double_t res; + + DUK_ASSERT_API_ENTRY(thr); + + duk_push_tval(thr, tv); + res = duk_to_number_m1(thr); + duk_pop_unsafe(thr); + return res; +#else + duk_double_t res; + duk_tval *tv_dst; + + DUK_ASSERT_API_ENTRY(thr); + DUK__ASSERT_SPACE(); + + tv_dst = thr->valstack_top++; + DUK_TVAL_SET_TVAL(tv_dst, tv); + DUK_TVAL_INCREF(thr, tv_dst); /* decref not necessary */ + res = duk_to_number_m1(thr); /* invalidates tv_dst */ + + tv_dst = --thr->valstack_top; + DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_dst)); + DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_dst)); /* plain number */ + DUK_TVAL_SET_UNDEFINED(tv_dst); /* valstack init policy */ + + return res; +#endif +} + /* XXX: combine all the integer conversions: they share everything * but the helper function for coercion. */ typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv); -DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t index, duk__toint_coercer coerce_func) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_hthread *thr, duk_idx_t idx, duk__toint_coercer coerce_func) { duk_tval *tv; duk_double_t d; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); + +#if defined(DUK_USE_FASTINT) + /* If argument is a fastint, guarantee that it remains one. + * There's no downgrade check for other cases. + */ + if (DUK_TVAL_IS_FASTINT(tv)) { + /* XXX: Unnecessary conversion back and forth. */ + return (duk_double_t) DUK_TVAL_GET_FASTINT(tv); + } +#endif d = coerce_func(thr, tv); /* XXX: fastint? */ /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */ return d; } -DUK_EXTERNAL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t index) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4 - * API return value coercion: custom +DUK_EXTERNAL duk_int_t duk_to_int(duk_hthread *thr, duk_idx_t idx) { + /* Value coercion (in stack): ToInteger(), E5 Section 9.4, + * API return value coercion: custom. */ - DUK_ASSERT_CTX_VALID(ctx); - (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger); - return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/); + DUK_ASSERT_API_ENTRY(thr); + (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger); + return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/); } -DUK_EXTERNAL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t index) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4 - * API return value coercion: custom +DUK_EXTERNAL duk_uint_t duk_to_uint(duk_hthread *thr, duk_idx_t idx) { + /* Value coercion (in stack): ToInteger(), E5 Section 9.4, + * API return value coercion: custom. */ - DUK_ASSERT_CTX_VALID(ctx); - (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger); - return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/); + DUK_ASSERT_API_ENTRY(thr); + (void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger); + return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/); } -DUK_EXTERNAL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_int32_t duk_to_int32(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_int32_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); ret = duk_js_toint32(thr, tv); /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(ctx, index); - DUK_TVAL_SET_FASTINT_I32_UPDREF(thr, tv, ret); /* side effects */ + tv = duk_require_tval(thr, idx); + DUK_TVAL_SET_I32_UPDREF(thr, tv, ret); /* side effects */ return ret; } -DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_uint32_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); ret = duk_js_touint32(thr, tv); /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(ctx, index); - DUK_TVAL_SET_FASTINT_U32_UPDREF(thr, tv, ret); /* side effects */ + tv = duk_require_tval(thr, idx); + DUK_TVAL_SET_U32_UPDREF(thr, tv, ret); /* side effects */ return ret; } -DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_uint16_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); ret = duk_js_touint16(thr, tv); /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */ - tv = duk_require_tval(ctx, index); - DUK_TVAL_SET_FASTINT_U32_UPDREF(thr, tv, ret); /* side effects */ + tv = duk_require_tval(thr, idx); + DUK_TVAL_SET_U32_UPDREF(thr, tv, ret); /* side effects */ return ret; } #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* Special coercion for Uint8ClampedArray. */ -DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index) { +DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx) { duk_double_t d; duk_double_t t; duk_uint8_t ret; + DUK_ASSERT_API_ENTRY(thr); + /* XXX: Simplify this algorithm, should be possible to come up with * a shorter and faster algorithm by inspecting IEEE representation * directly. */ - d = duk_to_number(ctx, index); + d = duk_to_number(thr, idx); if (d <= 0.0) { return 0; } else if (d >= 255) { @@ -16960,7 +21010,7 @@ DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index) } t = d - DUK_FLOOR(d); - if (t == 0.5) { + if (duk_double_equals(t, 0.5)) { /* Exact halfway, round to even. */ ret = (duk_uint8_t) d; ret = (ret + 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4 @@ -16974,119 +21024,233 @@ DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index) } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_EXTERNAL const char *duk_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL const char *duk_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + DUK_ASSERT_API_ENTRY(thr); - (void) duk_to_string(ctx, index); - return duk_require_lstring(ctx, index, out_len); + (void) duk_to_string(thr, idx); + DUK_ASSERT(duk_is_string(thr, idx)); + return duk_require_lstring(thr, idx, out_len); } -DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_hthread *thr, void *udata) { + DUK_CTX_ASSERT_VALID(thr); + DUK_UNREF(udata); - duk_to_string(ctx, -1); + (void) duk_to_string(thr, -1); return 1; } -DUK_EXTERNAL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL const char *duk_safe_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) { + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); + idx = duk_require_normalize_index(thr, idx); /* We intentionally ignore the duk_safe_call() return value and only * check the output type. This way we don't also need to check that * the returned value is indeed a string in the success case. */ - duk_dup(ctx, index); - (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/); - if (!duk_is_string(ctx, -1)) { + duk_dup(thr, idx); + (void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); + if (!duk_is_string(thr, -1)) { /* Error: try coercing error to string once. */ - (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/); - if (!duk_is_string(ctx, -1)) { + (void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); + if (!duk_is_string(thr, -1)) { /* Double error */ - duk_pop(ctx); - duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_ERROR); + duk_pop_unsafe(thr); + duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR); } else { ; } } else { + /* String; may be a symbol, accepted. */ ; } - DUK_ASSERT(duk_is_string(ctx, -1)); - DUK_ASSERT(duk_get_string(ctx, -1) != NULL); + DUK_ASSERT(duk_is_string(thr, -1)); - duk_replace(ctx, index); - return duk_get_lstring(ctx, index, out_len); + duk_replace(thr, idx); + DUK_ASSERT(duk_get_string(thr, idx) != NULL); + return duk_get_lstring(thr, idx, out_len); +} + +DUK_EXTERNAL const char *duk_to_stacktrace(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + idx = duk_require_normalize_index(thr, idx); + + /* The expected argument to the call is an Error object. The stack + * trace is extracted without an inheritance-based instanceof check + * so that one can also extract the stack trace of a foreign error + * created in another Realm. Accept only a string .stack property. + */ + if (duk_is_object(thr, idx)) { + (void) duk_get_prop_string(thr, idx, "stack"); + if (duk_is_string(thr, -1)) { + duk_replace(thr, idx); + } else { + duk_pop(thr); + } + } + + return duk_to_string(thr, idx); +} + +DUK_LOCAL duk_ret_t duk__safe_to_stacktrace_raw(duk_hthread *thr, void *udata) { + DUK_CTX_ASSERT_VALID(thr); + DUK_UNREF(udata); + + (void) duk_to_stacktrace(thr, -1); + + return 1; +} + +DUK_EXTERNAL const char *duk_safe_to_stacktrace(duk_hthread *thr, duk_idx_t idx) { + duk_int_t rc; + + DUK_ASSERT_API_ENTRY(thr); + idx = duk_require_normalize_index(thr, idx); + + duk_dup(thr, idx); + rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); + if (rc != 0) { + /* Coercion failed. Try to coerce the coercion itself error + * to a stack trace once. If that also fails, return a fixed, + * preallocated 'Error' string to avoid potential infinite loop. + */ + rc = duk_safe_call(thr, duk__safe_to_stacktrace_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/); + if (rc != 0) { + duk_pop_unsafe(thr); + duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR); + } + } + duk_replace(thr, idx); + + return duk_get_string(thr, idx); +} + +DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + + duk_to_primitive(thr, idx, DUK_HINT_STRING); /* needed for e.g. Symbol objects */ + h = duk_get_hstring(thr, idx); + if (h == NULL) { + /* The "is string?" check may seem unnecessary, but as things + * are duk_to_hstring() invokes ToString() which fails for + * symbols. But since symbols are already strings for Duktape + * C API, we check for that before doing the coercion. + */ + h = duk_to_hstring(thr, idx); + } + DUK_ASSERT(h != NULL); + return h; } #if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */ -DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t index) { - (void) duk_safe_to_string(ctx, index); - DUK_ASSERT(duk_is_string(ctx, index)); - DUK_ASSERT(duk_get_hstring(ctx, index) != NULL); - return duk_get_hstring(ctx, index); +DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + (void) duk_safe_to_string(thr, idx); + DUK_ASSERT(duk_is_string(thr, idx)); + DUK_ASSERT(duk_get_hstring(thr, idx) != NULL); + return duk_known_hstring(thr, idx); } #endif -/* Coerce top into Object.prototype.toString() output. */ -DUK_INTERNAL void duk_to_object_class_string_top(duk_context *ctx) { - duk_hthread *thr; - duk_uint_t typemask; - duk_hstring *h_strclass; +/* Push Object.prototype.toString() output for 'tv'. */ +DUK_INTERNAL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv, duk_bool_t avoid_side_effects) { + duk_hobject *h_obj; + duk_small_uint_t classnum; + duk_small_uint_t stridx; + duk_tval tv_tmp; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(tv != NULL); - typemask = duk_get_type_mask(ctx, -1); - if (typemask & DUK_TYPE_MASK_UNDEFINED) { - h_strclass = DUK_HTHREAD_STRING_UC_UNDEFINED(thr); - } else if (typemask & DUK_TYPE_MASK_NULL) { - h_strclass = DUK_HTHREAD_STRING_UC_NULL(thr); - } else { - duk_hobject *h_obj; + /* Stabilize 'tv', duk_push_literal() may trigger side effects. */ + DUK_TVAL_SET_TVAL(&tv_tmp, tv); + tv = &tv_tmp; - duk_to_object(ctx, -1); - h_obj = duk_get_hobject(ctx, -1); - DUK_ASSERT(h_obj != NULL); + /* Conceptually for any non-undefined/null value we should do a + * ToObject() coercion and look up @@toStringTag (from the object + * prototype) to see if a custom result should be used, with the + * exception of Arrays which are handled specially first. + * + * We'd like to avoid the actual conversion, but even for primitive + * types the prototype may have @@toStringTag. What's worse, the + * @@toStringTag property may be a getter that must get the object + * coerced value (not the prototype) as its 'this' binding. + * + * For now, do an actual object coercion. This could be avoided by + * doing a side effect free lookup to see if a getter would be invoked. + * If not, the value can be read directly and the object coercion could + * be avoided. This may not be worth it in practice, because + * Object.prototype.toString() is usually not performance critical. + */ - h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h_obj); + duk_push_literal(thr, "[object "); /* -> [ ... "[object" ] */ + + switch (DUK_TVAL_GET_TAG(tv)) { + case DUK_TAG_UNUSED: /* Treat like 'undefined', shouldn't happen. */ + case DUK_TAG_UNDEFINED: { + duk_push_hstring_stridx(thr, DUK_STRIDX_UC_UNDEFINED); + goto finish; + } + case DUK_TAG_NULL: { + duk_push_hstring_stridx(thr, DUK_STRIDX_UC_NULL); + goto finish; + } } - DUK_ASSERT(h_strclass != NULL); - duk_pop(ctx); - duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass)); + duk_push_tval(thr, tv); + tv = NULL; /* Invalidated by ToObject(). */ + h_obj = duk_to_hobject(thr, -1); + DUK_ASSERT(h_obj != NULL); + if (duk_js_isarray_hobject(h_obj)) { + stridx = DUK_STRIDX_UC_ARRAY; + } else { + /* [ ... "[object" obj ] */ + +#if defined(DUK_USE_SYMBOL_BUILTIN) + /* XXX: better handling with avoid_side_effects == 1; lookup tval + * without Proxy or getter side effects, and use it in sanitized + * form if it's a string. + */ + if (!avoid_side_effects) { + (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_TO_STRING_TAG); + if (duk_is_string_notsymbol(thr, -1)) { + duk_remove_m2(thr); + goto finish; + } + duk_pop_unsafe(thr); + } +#else + DUK_UNREF(avoid_side_effects); +#endif + + classnum = DUK_HOBJECT_GET_CLASS_NUMBER(h_obj); + stridx = DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum); + } + duk_pop_unsafe(thr); + duk_push_hstring_stridx(thr, stridx); + + finish: + /* [ ... "[object" tag ] */ + duk_push_literal(thr, "]"); + duk_concat(thr, 3); /* [ ... "[object" tag "]" ] -> [ ... res ] */ } -#if !defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL void duk_push_hobject_class_string(duk_context *ctx, duk_hobject *h) { - duk_hthread *thr; - duk_hstring *h_strclass; - - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(h != NULL); - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h); - DUK_ASSERT(h_strclass != NULL); - duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass)); -} -#endif /* !DUK_USE_PARANOID_ERRORS */ - /* XXX: other variants like uint, u32 etc */ -DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) { duk_tval *tv; duk_tval tv_tmp; duk_double_t d, dmin, dmax; duk_int_t res; duk_bool_t clamped = 0; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); d = duk_js_tointeger(thr, tv); /* E5 Section 9.4, ToInteger() */ @@ -17108,12 +21272,12 @@ DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t index, /* 'd' and 'res' agree here */ /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */ - tv = duk_get_tval(ctx, index); + tv = duk_get_tval(thr, idx); DUK_ASSERT(tv != NULL); /* not popped by side effect */ DUK_TVAL_SET_TVAL(&tv_tmp, tv); #if defined(DUK_USE_FASTINT) #if (DUK_INT_MAX <= 0x7fffffffL) - DUK_TVAL_SET_FASTINT_I32(tv, res); + DUK_TVAL_SET_I32(tv, res); #else /* Clamping needed if duk_int_t is 64 bits. */ if (res >= DUK_FASTINT_MIN && res <= DUK_FASTINT_MAX) { @@ -17133,85 +21297,102 @@ DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t index, /* coerced value is updated to value stack even when RangeError thrown */ if (clamped) { DUK_ERROR_RANGE(thr, DUK_STR_NUMBER_OUTSIDE_RANGE); + DUK_WO_NORETURN(return 0;); } } return res; } -DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t index, duk_idx_t minval, duk_idx_t maxval) { +DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_idx_t minval, duk_idx_t maxval) { duk_bool_t dummy; - return duk_to_int_clamped_raw(ctx, index, minval, maxval, &dummy); + + DUK_ASSERT_API_ENTRY(thr); + + return duk_to_int_clamped_raw(thr, idx, minval, maxval, &dummy); } -DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval) { - return duk_to_int_clamped_raw(ctx, index, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */ +DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval) { + DUK_ASSERT_API_ENTRY(thr); + return duk_to_int_clamped_raw(thr, idx, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */ } -DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - - tv = duk_require_tval(ctx, index); + idx = duk_require_normalize_index(thr, idx); + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_UNDEFINED: { - duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_UNDEFINED); + duk_push_hstring_stridx(thr, DUK_STRIDX_LC_UNDEFINED); break; } case DUK_TAG_NULL: { - duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL); + duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL); break; } case DUK_TAG_BOOLEAN: { if (DUK_TVAL_GET_BOOLEAN(tv)) { - duk_push_hstring_stridx(ctx, DUK_STRIDX_TRUE); + duk_push_hstring_stridx(thr, DUK_STRIDX_TRUE); } else { - duk_push_hstring_stridx(ctx, DUK_STRIDX_FALSE); + duk_push_hstring_stridx(thr, DUK_STRIDX_FALSE); } break; } case DUK_TAG_STRING: { - /* nop */ - goto skip_replace; - } - case DUK_TAG_OBJECT: { - duk_to_primitive(ctx, index, DUK_HINT_STRING); - return duk_to_string(ctx, index); /* Note: recursive call */ - } - case DUK_TAG_BUFFER: { - duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); - - /* Note: this allows creation of internal strings. */ - + /* Nop for actual strings, TypeError for Symbols. + * Because various internals rely on ToString() coercion of + * internal strings, -allow- (NOP) string coercion for hidden + * symbols. + */ +#if 1 + duk_hstring *h; + h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - duk_push_lstring(ctx, - (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h), - (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_STRING_COERCE_SYMBOL); + DUK_WO_NORETURN(goto skip_replace;); + } else { + goto skip_replace; + } +#else + goto skip_replace; +#endif break; } + case DUK_TAG_BUFFER: /* Go through Uint8Array.prototype.toString() for coercion. */ + case DUK_TAG_OBJECT: { + /* Plain buffers: go through ArrayBuffer.prototype.toString() + * for coercion. + * + * Symbol objects: duk_to_primitive() results in a plain symbol + * value, and duk_to_string() then causes a TypeError. + */ + duk_to_primitive(thr, idx, DUK_HINT_STRING); + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* ToPrimitive() must guarantee */ + DUK_ASSERT(!duk_is_object(thr, idx)); + return duk_to_string(thr, idx); /* Note: recursive call */ + } case DUK_TAG_POINTER: { void *ptr = DUK_TVAL_GET_POINTER(tv); if (ptr != NULL) { - duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) ptr); + duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) ptr); } else { /* Represent a null pointer as 'null' to be consistent with * the JX format variant. Native '%p' format for a NULL * pointer may be e.g. '(nil)'. */ - duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL); + duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL); } break; } case DUK_TAG_LIGHTFUNC: { /* Should match Function.prototype.toString() */ - duk_push_lightfunc_tostring(ctx, tv); + duk_push_lightfunc_tostring(thr, tv); break; } #if defined(DUK_USE_FASTINT) @@ -17221,8 +21402,8 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) { /* number */ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - duk_push_tval(ctx, tv); - duk_numconv_stringify(ctx, + duk_push_tval(thr, tv); + duk_numconv_stringify(thr, 10 /*radix*/, 0 /*precision:shortest*/, 0 /*force_exponential*/); @@ -17230,34 +21411,76 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) { } } - duk_replace(ctx, index); + duk_replace(thr, idx); skip_replace: - return duk_require_string(ctx, index); + DUK_ASSERT(duk_is_string(thr, idx)); + return duk_require_string(thr, idx); } -DUK_INTERNAL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t index) { +DUK_INTERNAL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx) { duk_hstring *ret; - DUK_ASSERT_CTX_VALID(ctx); - duk_to_string(ctx, index); - ret = duk_get_hstring(ctx, index); + + DUK_ASSERT_API_ENTRY(thr); + + duk_to_string(thr, idx); + ret = duk_get_hstring(thr, idx); DUK_ASSERT(ret != NULL); return ret; } -DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_uint_t mode) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_hstring *duk_to_hstring_m1(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + return duk_to_hstring(thr, -1); +} + +DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *ret; + + DUK_ASSERT_API_ENTRY(thr); + + ret = duk_get_hstring(thr, idx); + if (DUK_UNLIKELY(ret && DUK_HSTRING_HAS_SYMBOL(ret))) { + return ret; + } + return duk_to_hstring(thr, idx); +} + +/* Convert a plain buffer or any buffer object into a string, using the buffer + * bytes 1:1 in the internal string representation. For views the active byte + * slice (not element slice interpreted as an initializer) is used. This is + * necessary in Duktape 2.x because ToString(plainBuffer) no longer creates a + * string with the same bytes as in the buffer but rather (usually) + * '[object ArrayBuffer]'. + */ +DUK_EXTERNAL const char *duk_buffer_to_string(duk_hthread *thr, duk_idx_t idx) { + void *ptr_src; + duk_size_t len; + const char *res; + + DUK_ASSERT_API_ENTRY(thr); + + idx = duk_require_normalize_index(thr, idx); + + ptr_src = duk_require_buffer_data(thr, idx, &len); + DUK_ASSERT(ptr_src != NULL || len == 0); + + res = duk_push_lstring(thr, (const char *) ptr_src, len); + duk_replace(thr, idx); + return res; +} + +DUK_EXTERNAL void *duk_to_buffer_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, duk_uint_t mode) { duk_hbuffer *h_buf; const duk_uint8_t *src_data; duk_size_t src_size; duk_uint8_t *dst_data; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); + idx = duk_require_normalize_index(thr, idx); - h_buf = duk_get_hbuffer(ctx, index); + h_buf = duk_get_hbuffer(thr, idx); if (h_buf != NULL) { /* Buffer is kept as is, with the fixed/dynamic nature of the * buffer only changed if requested. An external buffer @@ -17283,22 +21506,17 @@ DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t index, duk_size } else { /* Non-buffer value is first ToString() coerced, then converted * to a buffer (fixed buffer is used unless a dynamic buffer is - * explicitly requested). + * explicitly requested). Symbols are rejected with a TypeError. + * XXX: C API could maybe allow symbol-to-buffer coercion? */ - - src_data = (const duk_uint8_t *) duk_to_lstring(ctx, index, &src_size); + src_data = (const duk_uint8_t *) duk_to_lstring(thr, idx, &src_size); } - dst_data = (duk_uint8_t *) duk_push_buffer(ctx, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/); - if (DUK_LIKELY(src_size > 0)) { - /* When src_size == 0, src_data may be NULL (if source - * buffer is dynamic), and dst_data may be NULL (if - * target buffer is dynamic). Avoid zero-size memcpy() - * with an invalid pointer. - */ - DUK_MEMCPY((void *) dst_data, (const void *) src_data, (size_t) src_size); - } - duk_replace(ctx, index); + dst_data = (duk_uint8_t *) duk_push_buffer(thr, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/); + /* dst_data may be NULL if size is zero. */ + duk_memcpy_unsafe((void *) dst_data, (const void *) src_data, (size_t) src_size); + + duk_replace(thr, idx); skip_copy: if (out_size) { @@ -17307,15 +21525,14 @@ DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t index, duk_size return dst_data; } -DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL void *duk_to_pointer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; void *res; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - - tv = duk_require_tval(ctx, index); + idx = duk_require_normalize_index(thr, idx); + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { @@ -17353,80 +21570,119 @@ DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) { break; } - duk_push_pointer(ctx, res); - duk_replace(ctx, index); + duk_push_pointer(thr, res); + duk_replace(thr, idx); return res; } -DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL void duk__push_func_from_lightfunc(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) { + duk_idx_t nargs; + duk_uint_t flags = 0; /* shared flags for a subset of types */ + duk_small_uint_t lf_len; + duk_hnatfunc *nf; + + nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); + if (nargs == DUK_LFUNC_NARGS_VARARGS) { + nargs = (duk_idx_t) DUK_VARARGS; + } + + flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_CONSTRUCTABLE | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_NATFUNC | + DUK_HOBJECT_FLAG_NEWENV | + DUK_HOBJECT_FLAG_STRICT | + DUK_HOBJECT_FLAG_NOTAIL | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); + (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE); + + lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); + if ((duk_idx_t) lf_len != nargs) { + /* Explicit length is only needed if it differs from 'nargs'. */ + duk_push_int(thr, (duk_int_t) lf_len); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); + } + +#if defined(DUK_USE_FUNC_NAME_PROPERTY) + duk_push_lightfunc_name_raw(thr, func, lf_flags); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); +#endif + + nf = duk_known_hnatfunc(thr, -1); + nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); +} + +DUK_EXTERNAL void duk_to_object(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_uint_t flags = 0; /* shared flags for a subset of types */ duk_small_int_t proto = 0; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - - tv = duk_require_tval(ctx, index); + idx = duk_require_normalize_index(thr, idx); + tv = DUK_GET_TVAL_POSIDX(thr, idx); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { +#if !defined(DUK_USE_BUFFEROBJECT_SUPPORT) + case DUK_TAG_BUFFER: /* With no bufferobject support, don't object coerce. */ +#endif case DUK_TAG_UNDEFINED: case DUK_TAG_NULL: { DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE); + DUK_WO_NORETURN(return;); break; } case DUK_TAG_BOOLEAN: { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN); proto = DUK_BIDX_BOOLEAN_PROTOTYPE; goto create_object; } case DUK_TAG_STRING: { - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); - proto = DUK_BIDX_STRING_PROTOTYPE; + duk_hstring *h; + h = DUK_TVAL_GET_STRING(tv); + DUK_ASSERT(h != NULL); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_SYMBOL); + proto = DUK_BIDX_SYMBOL_PROTOTYPE; + } else { + flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); + proto = DUK_BIDX_STRING_PROTOTYPE; + } goto create_object; } case DUK_TAG_OBJECT: { /* nop */ break; } +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) case DUK_TAG_BUFFER: { - /* A plain buffer coerces to a Duktape.Buffer because it's the - * object counterpart of the plain buffer value. But it might - * still make more sense to produce an ArrayBuffer here? + /* A plain buffer object coerces to a full ArrayBuffer which + * is not fully transparent behavior (ToObject() should be a + * nop for an object). This behavior matches lightfuncs which + * also coerce to an equivalent Function object. There are + * also downsides to defining ToObject(plainBuffer) as a no-op; + * for example duk_to_hobject() could result in a NULL pointer. */ + duk_hbuffer *h_buf; - duk_hbufferobject *h_bufobj; - duk_hbuffer *h_val; - - h_val = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h_val != NULL); - - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), - DUK_BIDX_BUFFER_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); - DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject *) h_bufobj)); - DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj)); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - DUK_ASSERT(h_bufobj->offset == 0); - h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val); - DUK_ASSERT(h_bufobj->shift == 0); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8); - - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + h_buf = DUK_TVAL_GET_BUFFER(tv); + DUK_ASSERT(h_buf != NULL); + duk_hbufobj_push_uint8array_from_plain(thr, h_buf); goto replace_value; } +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ case DUK_TAG_POINTER: { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER); proto = DUK_BIDX_POINTER_PROTOTYPE; goto create_object; @@ -17434,50 +21690,18 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { case DUK_TAG_LIGHTFUNC: { /* Lightfunc coerces to a Function instance with concrete * properties. Since 'length' is virtual for Duktape/C - * functions, don't need to define that. + * functions, don't need to define that. The result is made + * extensible to mimic what happens to strings in object + * coercion: * - * The result is made extensible to mimic what happens to - * strings: * > Object.isExtensible(Object('foo')) * true */ duk_small_uint_t lf_flags; - duk_idx_t nargs; - duk_small_uint_t lf_len; duk_c_function func; - duk_hnativefunction *nf; DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); - - nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); - if (nargs == DUK_LFUNC_NARGS_VARARGS) { - nargs = (duk_idx_t) DUK_VARARGS; - } - flags = DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_CONSTRUCTABLE | - DUK_HOBJECT_FLAG_NATIVEFUNCTION | - DUK_HOBJECT_FLAG_NEWENV | - DUK_HOBJECT_FLAG_STRICT | - DUK_HOBJECT_FLAG_NOTAIL | - /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */ - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, nargs, flags); - - lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); - if ((duk_idx_t) lf_len != nargs) { - /* Explicit length is only needed if it differs from 'nargs'. */ - duk_push_int(ctx, (duk_int_t) lf_len); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); - } - duk_push_lightfunc_name(ctx, tv); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); - - nf = duk_get_hnativefunction(ctx, -1); - DUK_ASSERT(nf != NULL); - nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags); - - /* Enable DUKFUNC exotic behavior once properties are set up. */ - DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject *) nf); + duk__push_func_from_lightfunc(thr, func, lf_flags); goto replace_value; } #if defined(DUK_USE_FASTINT) @@ -17487,15 +21711,17 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER); + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER); proto = DUK_BIDX_NUMBER_PROTOTYPE; goto create_object; } } + DUK_ASSERT(duk_is_object(thr, idx)); return; create_object: - (void) duk_push_object_helper(ctx, flags, proto); + (void) duk_push_object_helper(thr, flags, proto); /* Note: Boolean prototype's internal value property is not writable, * but duk_xdef_prop_stridx() disregards the write protection. Boolean @@ -17504,49 +21730,55 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) { * String and buffer special behaviors are already enabled which is not * ideal, but a write to the internal value is not affected by them. */ - duk_dup(ctx, index); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); + duk_dup(thr, idx); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); replace_value: - duk_replace(ctx, index); + duk_replace(thr, idx); + DUK_ASSERT(duk_is_object(thr, idx)); +} + +DUK_INTERNAL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *ret; + + DUK_ASSERT_API_ENTRY(thr); + + duk_to_object(thr, idx); + ret = duk_known_hobject(thr, idx); + return ret; } /* * Type checking */ -DUK_LOCAL duk_bool_t duk__tag_check(duk_context *ctx, duk_idx_t index, duk_small_uint_t tag) { +DUK_LOCAL duk_bool_t duk__tag_check(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t tag) { duk_tval *tv; - tv = duk_get_tval(ctx, index); - if (!tv) { - return 0; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); return (DUK_TVAL_GET_TAG(tv) == tag); } -DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_context *ctx, duk_idx_t index, duk_uint_t flag_mask) { +DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_hthread *thr, duk_idx_t idx, duk_uint_t flag_mask) { duk_hobject *obj; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, index); + obj = duk_get_hobject(thr, idx); if (obj) { return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0); } return 0; } -DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; +DUK_INTERNAL duk_int_t duk_get_type_tval(duk_tval *tv) { + DUK_ASSERT(tv != NULL); - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_get_tval(ctx, index); - if (!tv) { - return DUK_TYPE_NONE; - } +#if defined(DUK_USE_PACKED_TVAL) switch (DUK_TVAL_GET_TAG(tv)) { + case DUK_TAG_UNUSED: + return DUK_TYPE_NONE; case DUK_TAG_UNDEFINED: return DUK_TYPE_UNDEFINED; case DUK_TAG_NULL: @@ -17572,11 +21804,26 @@ DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t index) { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); return DUK_TYPE_NUMBER; } - DUK_UNREACHABLE(); +#else /* DUK_USE_PACKED_TVAL */ + DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv)); + DUK_ASSERT(sizeof(duk__type_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1); + return (duk_int_t) duk__type_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN]; +#endif /* DUK_USE_PACKED_TVAL */ +} + +DUK_EXTERNAL duk_int_t duk_get_type(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + + return duk_get_type_tval(tv); } #if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS) -DUK_LOCAL const char *duk__type_names[] = { +DUK_LOCAL const char * const duk__type_names[] = { "none", "undefined", "null", @@ -17589,33 +21836,58 @@ DUK_LOCAL const char *duk__type_names[] = { "lightfunc" }; -DUK_INTERNAL const char *duk_get_type_name(duk_context *ctx, duk_idx_t index) { +DUK_INTERNAL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx) { duk_int_t type_tag; - type_tag = duk_get_type(ctx, index); + DUK_ASSERT_API_ENTRY(thr); + + type_tag = duk_get_type(thr, idx); DUK_ASSERT(type_tag >= DUK_TYPE_MIN && type_tag <= DUK_TYPE_MAX); DUK_ASSERT(DUK_TYPE_MIN == 0 && sizeof(duk__type_names) / sizeof(const char *) == DUK_TYPE_MAX + 1); return duk__type_names[type_tag]; } -#endif +#endif /* DUK_USE_VERBOSE_ERRORS && DUK_USE_PARANOID_ERRORS */ -DUK_EXTERNAL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t index, duk_int_t type) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + duk_hobject *obj; - return (duk_get_type(ctx, index) == type) ? 1 : 0; + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + + switch (DUK_TVAL_GET_TAG(tv)) { + case DUK_TAG_OBJECT: + obj = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(obj != NULL); + return DUK_HOBJECT_GET_CLASS_NUMBER(obj); + case DUK_TAG_BUFFER: + /* Buffers behave like Uint8Array objects. */ + return DUK_HOBJECT_CLASS_UINT8ARRAY; + case DUK_TAG_LIGHTFUNC: + /* Lightfuncs behave like Function objects. */ + return DUK_HOBJECT_CLASS_FUNCTION; + default: + /* Primitive or UNUSED, no class number. */ + return DUK_HOBJECT_CLASS_NONE; + } } -DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; +DUK_EXTERNAL duk_bool_t duk_check_type(duk_hthread *thr, duk_idx_t idx, duk_int_t type) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); + return (duk_get_type(thr, idx) == type) ? 1 : 0; +} - tv = duk_get_tval(ctx, index); - if (!tv) { - return DUK_TYPE_MASK_NONE; - } +DUK_INTERNAL duk_uint_t duk_get_type_mask_tval(duk_tval *tv) { + DUK_ASSERT(tv != NULL); + +#if defined(DUK_USE_PACKED_TVAL) switch (DUK_TVAL_GET_TAG(tv)) { + case DUK_TAG_UNUSED: + return DUK_TYPE_MASK_NONE; case DUK_TAG_UNDEFINED: return DUK_TYPE_MASK_UNDEFINED; case DUK_TAG_NULL: @@ -17641,73 +21913,70 @@ DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t index) { DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); return DUK_TYPE_MASK_NUMBER; } - DUK_UNREACHABLE(); +#else /* DUK_USE_PACKED_TVAL */ + DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv)); + DUK_ASSERT(sizeof(duk__type_mask_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1); + return duk__type_mask_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN]; +#endif /* DUK_USE_PACKED_TVAL */ } -DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t index, duk_uint_t mask) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - if (duk_get_type_mask(ctx, index) & mask) { + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + + return duk_get_type_mask_tval(tv); +} + +DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t mask) { + DUK_ASSERT_API_ENTRY(thr); + + if (DUK_LIKELY((duk_get_type_mask(thr, idx) & mask) != 0U)) { return 1; } if (mask & DUK_TYPE_MASK_THROW) { DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE); - DUK_UNREACHABLE(); + DUK_WO_NORETURN(return 0;); } return 0; } -DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_UNDEFINED); +DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_UNDEFINED); } -DUK_EXTERNAL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_NULL); +DUK_EXTERNAL duk_bool_t duk_is_null(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_NULL); } -DUK_EXTERNAL duk_bool_t duk_is_null_or_undefined(duk_context *ctx, duk_idx_t index) { - duk_tval *tv; - duk_small_uint_t tag; - - DUK_ASSERT_CTX_VALID(ctx); - - tv = duk_get_tval(ctx, index); - if (!tv) { - return 0; - } - tag = DUK_TVAL_GET_TAG(tv); - return (tag == DUK_TAG_UNDEFINED) || (tag == DUK_TAG_NULL); +DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_BOOLEAN); } -DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_BOOLEAN); -} - -DUK_EXTERNAL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_is_number(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* * Number is special because it doesn't have a specific * tag in the 8-byte representation. */ - /* XXX: shorter version for 12-byte representation? */ + /* XXX: shorter version for unpacked representation? */ - tv = duk_get_tval(ctx, index); - if (!tv) { - return 0; - } + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); return DUK_TVAL_IS_NUMBER(tv); } -DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_is_nan(duk_hthread *thr, duk_idx_t idx) { /* XXX: This will now return false for non-numbers, even though they would * coerce to NaN (as a general rule). In particular, duk_get_number() * returns a NaN for non-numbers, so should this function also return @@ -17716,103 +21985,197 @@ DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t index) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (!tv || !DUK_TVAL_IS_NUMBER(tv)) { + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + + /* XXX: for packed duk_tval an explicit "is number" check is unnecessary */ + if (!DUK_TVAL_IS_NUMBER(tv)) { return 0; } - return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv)); + return (duk_bool_t) DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv)); } -DUK_EXTERNAL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_STRING); +DUK_EXTERNAL duk_bool_t duk_is_string(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_STRING); } -DUK_EXTERNAL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_OBJECT); +DUK_INTERNAL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk_get_hstring_notsymbol(thr, idx) != NULL; } -DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_BUFFER); +DUK_EXTERNAL duk_bool_t duk_is_object(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_OBJECT); } -DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_POINTER); +DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_BUFFER); } -DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, index, DUK_TAG_LIGHTFUNC); +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_BUFFER(tv)) { + return 1; + } else if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + if (DUK_HOBJECT_IS_BUFOBJ(h)) { + return 1; + } + } + return 0; +} +#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ +DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + + return duk_is_buffer(thr, idx); } -DUK_EXTERNAL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t index) { - duk_hobject *obj; +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_POINTER); +} - obj = duk_get_hobject(ctx, index); - if (obj) { - return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0); +DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__tag_check(thr, idx, DUK_TAG_LIGHTFUNC); +} + +DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_hthread *thr, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_API_ENTRY(thr); + h = duk_get_hstring(thr, idx); + /* Use DUK_LIKELY() here because caller may be more likely to type + * check an expected symbol than not. + */ + if (DUK_LIKELY(h != NULL && DUK_HSTRING_HAS_SYMBOL(h))) { + return 1; } return 0; } -DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t index) { +/* IsArray(), returns true for Array instance or Proxy of Array instance. */ +DUK_EXTERNAL duk_bool_t duk_is_array(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_LIGHTFUNC(tv)) { + tv = duk_get_tval(thr, idx); + if (tv) { + return duk_js_isarray(tv); + } + return 0; +} + +DUK_EXTERNAL duk_bool_t duk_is_function(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h; + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0; + } + if (DUK_TVAL_IS_LIGHTFUNC(tv)) { return 1; } - return duk__obj_flag_any_default_false(ctx, - index, - DUK_HOBJECT_FLAG_COMPILEDFUNCTION | - DUK_HOBJECT_FLAG_NATIVEFUNCTION | - DUK_HOBJECT_FLAG_BOUND); + return 0; } -DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__obj_flag_any_default_false(ctx, - index, - DUK_HOBJECT_FLAG_NATIVEFUNCTION); +DUK_INTERNAL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv) { + DUK_ASSERT_API_ENTRY(thr); + + DUK_UNREF(thr); + + if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h; + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0; + } + if (DUK_TVAL_IS_LIGHTFUNC(tv)) { + return 1; + } + return 0; } -DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__obj_flag_any_default_false(ctx, - index, - DUK_HOBJECT_FLAG_COMPILEDFUNCTION); -} - -DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__obj_flag_any_default_false(ctx, - index, - DUK_HOBJECT_FLAG_BOUND); -} - -DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__obj_flag_any_default_false(ctx, - index, - DUK_HOBJECT_FLAG_THREAD); -} - -DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_is_constructable(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BUFFER(tv)) { + tv = duk_get_tval_or_unused(thr, idx); + if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h; + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + return DUK_HOBJECT_HAS_CONSTRUCTABLE(h) ? 1 : 0; + } + if (DUK_TVAL_IS_LIGHTFUNC(tv)) { + return 1; + } + return 0; +} + +DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__obj_flag_any_default_false(thr, + idx, + DUK_HOBJECT_FLAG_NATFUNC); +} + +DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__obj_flag_any_default_false(thr, + idx, + DUK_HOBJECT_FLAG_COMPFUNC); +} + +DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk__obj_flag_any_default_false(thr, + idx, + DUK_HOBJECT_FLAG_BOUNDFUNC); +} + +DUK_EXTERNAL duk_bool_t duk_is_thread(duk_hthread *thr, duk_idx_t idx) { + duk_hobject *obj; + + DUK_ASSERT_API_ENTRY(thr); + + obj = duk_get_hobject(thr, idx); + if (obj) { + return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_THREAD ? 1 : 0); + } + return 0; +} + +DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_BUFFER(tv)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_HAS_DYNAMIC(h) ? 0 : 1); @@ -17820,13 +22183,14 @@ DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t index) { return 0; } -DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BUFFER(tv)) { + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_BUFFER(tv)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0); @@ -17834,13 +22198,14 @@ DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t index) return 0; } -DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t index) { +DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval(ctx, index); - if (tv && DUK_TVAL_IS_BUFFER(tv)) { + tv = duk_get_tval_or_unused(thr, idx); + DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_BUFFER(tv)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); return (DUK_HBUFFER_HAS_DYNAMIC(h) && DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0); @@ -17848,20 +22213,22 @@ DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t index return 0; } -DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_hthread *thr, duk_idx_t idx) { duk_hobject *h; duk_uint_t sanity; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hobject(ctx, index); + h = duk_get_hobject(thr, idx); sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; do { if (!h) { return DUK_ERR_NONE; } + + /* XXX: something more convenient? */ + if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) { return DUK_ERR_EVAL_ERROR; } @@ -17894,24 +22261,21 @@ DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t index) * Pushers */ -DUK_INTERNAL void duk_push_tval(duk_context *ctx, duk_tval *tv) { - duk_hthread *thr; +DUK_INTERNAL void duk_push_tval(duk_hthread *thr, duk_tval *tv) { duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(tv != NULL); - thr = (duk_hthread *) ctx; + DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; DUK_TVAL_SET_TVAL(tv_slot, tv); DUK_TVAL_INCREF(thr, tv); /* no side effects */ } -DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_undefined(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; DUK__CHECK_SPACE(); /* Because value stack init policy is 'undefined above top', @@ -17921,60 +22285,50 @@ DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) { DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); } -DUK_EXTERNAL void duk_push_null(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_null(duk_hthread *thr) { duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; DUK_TVAL_SET_NULL(tv_slot); } -DUK_EXTERNAL void duk_push_boolean(duk_context *ctx, duk_bool_t val) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_boolean(duk_hthread *thr, duk_bool_t val) { duk_tval *tv_slot; duk_small_int_t b; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); b = (val ? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */ tv_slot = thr->valstack_top++; DUK_TVAL_SET_BOOLEAN(tv_slot, b); } -DUK_EXTERNAL void duk_push_true(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_true(duk_hthread *thr) { duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot); } -DUK_EXTERNAL void duk_push_false(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_false(duk_hthread *thr) { duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot); } /* normalize NaN which may not match our canonical internal NaN */ -DUK_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_number(duk_hthread *thr, duk_double_t val) { duk_tval *tv_slot; duk_double_union du; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); du.d = val; DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); @@ -17982,17 +22336,15 @@ DUK_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) { DUK_TVAL_SET_NUMBER(tv_slot, du.d); } -DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) { +DUK_EXTERNAL void duk_push_int(duk_hthread *thr, duk_int_t val) { #if defined(DUK_USE_FASTINT) - duk_hthread *thr; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; #if DUK_INT_MAX <= 0x7fffffffL - DUK_TVAL_SET_FASTINT_I32(tv_slot, (duk_int32_t) val); + DUK_TVAL_SET_I32(tv_slot, (duk_int32_t) val); #else if (val >= DUK_FASTINT_MIN && val <= DUK_FASTINT_MAX) { DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val); @@ -18002,12 +22354,10 @@ DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) { } #endif #else /* DUK_USE_FASTINT */ - duk_hthread *thr; duk_tval *tv_slot; duk_double_t d; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); d = (duk_double_t) val; tv_slot = thr->valstack_top++; @@ -18015,17 +22365,15 @@ DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) { #endif /* DUK_USE_FASTINT */ } -DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) { +DUK_EXTERNAL void duk_push_uint(duk_hthread *thr, duk_uint_t val) { #if defined(DUK_USE_FASTINT) - duk_hthread *thr; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; #if DUK_UINT_MAX <= 0xffffffffUL - DUK_TVAL_SET_FASTINT_U32(tv_slot, (duk_uint32_t) val); + DUK_TVAL_SET_U32(tv_slot, (duk_uint32_t) val); #else if (val <= DUK_FASTINT_MAX) { /* val is unsigned so >= 0 */ /* XXX: take advantage of val being unsigned, no need to mask */ @@ -18036,12 +22384,10 @@ DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) { } #endif #else /* DUK_USE_FASTINT */ - duk_hthread *thr; duk_tval *tv_slot; duk_double_t d; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); d = (duk_double_t) val; tv_slot = thr->valstack_top++; @@ -18049,13 +22395,11 @@ DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) { #endif /* DUK_USE_FASTINT */ } -DUK_EXTERNAL void duk_push_nan(duk_context *ctx) { - duk_hthread *thr; +DUK_EXTERNAL void duk_push_nan(duk_hthread *thr) { duk_tval *tv_slot; duk_double_union du; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); DUK_DBLUNION_SET_NAN(&du); DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du)); @@ -18063,33 +22407,31 @@ DUK_EXTERNAL void duk_push_nan(duk_context *ctx) { DUK_TVAL_SET_NUMBER(tv_slot, du.d); } -DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_push_lstring(duk_hthread *thr, const char *str, duk_size_t len) { duk_hstring *h; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* check stack before interning (avoid hanging temp) */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + /* Check stack before interning (avoid hanging temp). */ + DUK__CHECK_SPACE(); /* NULL with zero length represents an empty string; NULL with higher - * length is also now trated like an empty string although it is + * length is also now treated like an empty string although it is * a bit dubious. This is unlike duk_push_string() which pushes a * 'null' if the input string is a NULL. */ - if (!str) { - len = 0; + if (DUK_UNLIKELY(str == NULL)) { + len = 0U; } - /* Check for maximum string length */ - if (len > DUK_HSTRING_MAX_BYTELEN) { + /* Check for maximum string length. */ + if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) { DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG); + DUK_WO_NORETURN(return NULL;); } - h = duk_heap_string_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); + h = duk_heap_strtable_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); DUK_ASSERT(h != NULL); tv_slot = thr->valstack_top++; @@ -18099,112 +22441,83 @@ DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk return (const char *) DUK_HSTRING_GET_DATA(h); } -DUK_EXTERNAL const char *duk_push_string(duk_context *ctx, const char *str) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL const char *duk_push_string(duk_hthread *thr, const char *str) { + DUK_ASSERT_API_ENTRY(thr); if (str) { - return duk_push_lstring(ctx, str, DUK_STRLEN(str)); + return duk_push_lstring(thr, str, DUK_STRLEN(str)); } else { - duk_push_null(ctx); + duk_push_null(thr); return NULL; } } -#ifdef DUK_USE_FILE_IO -/* This is a bit clunky because it is ANSI C portable. Should perhaps - * relocate to another file because this is potentially platform - * dependent. - */ -DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_file *f = NULL; - char *buf; - long sz; /* ANSI C typing */ - - DUK_ASSERT_CTX_VALID(ctx); - - if (!path) { - goto fail; - } - f = DUK_FOPEN(path, "rb"); - if (!f) { - goto fail; - } - if (DUK_FSEEK(f, 0, SEEK_END) < 0) { - goto fail; - } - sz = DUK_FTELL(f); - if (sz < 0) { - goto fail; - } - if (DUK_FSEEK(f, 0, SEEK_SET) < 0) { - goto fail; - } - buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz); - DUK_ASSERT(buf != NULL); - if ((duk_size_t) DUK_FREAD(buf, 1, (size_t) sz, f) != (duk_size_t) sz) { - goto fail; - } - (void) DUK_FCLOSE(f); /* ignore fclose() error */ - f = NULL; - return duk_to_string(ctx, -1); - - fail: - if (f) { - DUK_FCLOSE(f); - } - - if (flags != 0) { - DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */ - duk_push_undefined(ctx); - } else { - /* XXX: string not shared because it is conditional */ - DUK_ERROR_TYPE(thr, "read file error"); - } - return NULL; -} -#else -DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(path); - - if (flags != 0) { - DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */ - duk_push_undefined(ctx); - } else { - /* XXX: string not shared because it is conditional */ - DUK_ERROR_UNSUPPORTED(thr, "file I/O disabled"); - } - return NULL; -} -#endif /* DUK_USE_FILE_IO */ - -DUK_EXTERNAL void duk_push_pointer(duk_context *ctx, void *val) { - duk_hthread *thr; +#if !defined(DUK_USE_PREFER_SIZE) +#if defined(DUK_USE_LITCACHE_SIZE) +DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) { + duk_hstring *h; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(str != NULL); + DUK_ASSERT(str[len] == (char) 0); + + /* Check for maximum string length. */ + if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) { + DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG); + DUK_WO_NORETURN(return NULL;); + } + + h = duk_heap_strtable_intern_literal_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); + DUK_ASSERT(h != NULL); + + tv_slot = thr->valstack_top++; + DUK_TVAL_SET_STRING(tv_slot, h); + DUK_HSTRING_INCREF(thr, h); /* no side effects */ + + return (const char *) DUK_HSTRING_GET_DATA(h); +} +#else /* DUK_USE_LITCACHE_SIZE */ +DUK_EXTERNAL const char *duk_push_literal_raw(duk_hthread *thr, const char *str, duk_size_t len) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(str != NULL); + DUK_ASSERT(str[len] == (char) 0); + + return duk_push_lstring(thr, str, len); +} +#endif /* DUK_USE_LITCACHE_SIZE */ +#endif /* !DUK_USE_PREFER_SIZE */ + +DUK_EXTERNAL void duk_push_pointer(duk_hthread *thr, void *val) { + duk_tval *tv_slot; + + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); tv_slot = thr->valstack_top++; DUK_TVAL_SET_POINTER(tv_slot, val); } -DUK_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t check_object_coercible) { - duk_hthread *thr; +DUK_INTERNAL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i) { + duk_hstring *h_tmp; + + DUK_ASSERT_API_ENTRY(thr); + + /* XXX: this could be a direct DUK_SPRINTF to a buffer followed by duk_push_string() */ + duk_push_uint(thr, (duk_uint_t) i); + h_tmp = duk_to_hstring_m1(thr); + DUK_ASSERT(h_tmp != NULL); + return h_tmp; +} + +DUK_LOCAL void duk__push_this_helper(duk_hthread *thr, duk_small_uint_t check_object_coercible) { duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */ - thr = (duk_hthread *) ctx; - DUK_ASSERT(thr->callstack_top <= thr->callstack_size); DUK__CHECK_SPACE(); DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* because of valstack init policy */ tv_slot = thr->valstack_top++; - if (DUK_UNLIKELY(thr->callstack_top == 0)) { + if (DUK_UNLIKELY(thr->callstack_curr == NULL)) { if (check_object_coercible) { goto type_error; } @@ -18228,139 +22541,162 @@ DUK_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t check_ob type_error: DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_push_this(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_push_this(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 0 /*check_object_coercible*/); + duk__push_this_helper(thr, 0 /*check_object_coercible*/); } -DUK_INTERNAL void duk_push_this_check_object_coercible(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_INTERNAL void duk_push_this_check_object_coercible(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 1 /*check_object_coercible*/); + duk__push_this_helper(thr, 1 /*check_object_coercible*/); } -DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx) { +DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr) { duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 1 /*check_object_coercible*/); - duk_to_object(ctx, -1); - h = duk_get_hobject(ctx, -1); + duk__push_this_helper(thr, 1 /*check_object_coercible*/); + h = duk_to_hobject(thr, -1); DUK_ASSERT(h != NULL); return h; } -DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx) { - duk_hstring *h; +DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); - - duk__push_this_helper(ctx, 1 /*check_object_coercible*/); - duk_to_string(ctx, -1); - h = duk_get_hstring(ctx, -1); - DUK_ASSERT(h != NULL); - return h; + duk__push_this_helper(thr, 1 /*check_object_coercible*/); + return duk_to_hstring_m1(thr); /* This will reject all Symbol values; accepts Symbol objects. */ } -DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx) { - duk_hthread *thr; - - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->callstack_top > 0); /* caller required to know */ + DUK_ASSERT(thr->callstack_curr != NULL); /* caller required to know */ DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* consequence of above */ DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack); /* 'this' binding exists */ return thr->valstack_bottom - 1; } -DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_push_new_target(duk_hthread *thr) { duk_activation *act; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); - DUK_ASSERT_DISABLE(thr->callstack_top >= 0); - DUK_ASSERT(thr->callstack_top <= thr->callstack_size); + DUK_ASSERT_API_ENTRY(thr); - act = duk_hthread_get_current_activation(thr); - if (act) { - duk_push_tval(ctx, &act->tv_func); + /* https://www.ecma-international.org/ecma-262/6.0/#sec-meta-properties-runtime-semantics-evaluation + * https://www.ecma-international.org/ecma-262/6.0/#sec-getnewtarget + * + * No newTarget support now, so as a first approximation + * use the resolved (non-bound) target function. + * + * Check CONSTRUCT flag from current function, or if running + * direct eval, from a non-direct-eval parent (with possibly + * more than one nested direct eval). An alternative to this + * would be to store [[NewTarget]] as a hidden symbol of the + * lexical scope, and then just look up that variable. + * + * Calls from the application will either be for an empty + * call stack, or a Duktape/C function as the top activation. + */ + + act = thr->callstack_curr; + for (;;) { + if (act == NULL) { + break; + } + + if (act->flags & DUK_ACT_FLAG_CONSTRUCT) { + duk_push_tval(thr, &act->tv_func); + return; + } else if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) { + act = act->parent; + } else { + break; + } + } + + duk_push_undefined(thr); +} + +DUK_EXTERNAL void duk_push_current_function(duk_hthread *thr) { + duk_activation *act; + + DUK_ASSERT_API_ENTRY(thr); + + act = thr->callstack_curr; + if (act != NULL) { + duk_push_tval(thr, &act->tv_func); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -DUK_EXTERNAL void duk_push_current_thread(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); +DUK_EXTERNAL void duk_push_current_thread(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); if (thr->heap->curr_thread) { - duk_push_hobject(ctx, (duk_hobject *) thr->heap->curr_thread); + duk_push_hobject(thr, (duk_hobject *) thr->heap->curr_thread); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -DUK_EXTERNAL void duk_push_global_object(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_push_global_object(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL); + duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL); } /* XXX: size optimize */ -DUK_LOCAL void duk__push_stash(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE)) { +DUK_LOCAL void duk__push_stash(duk_hthread *thr) { + if (!duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE)) { DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use")); - duk_pop(ctx); - duk_push_object_internal(ctx); - duk_dup_top(ctx); - duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C); /* [ ... parent stash stash ] -> [ ... parent stash ] */ + duk_pop_unsafe(thr); + duk_push_bare_object(thr); + duk_dup_top(thr); + duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C); /* [ ... parent stash stash ] -> [ ... parent stash ] */ } - duk_remove(ctx, -2); + duk_remove_m2(thr); } -DUK_EXTERNAL void duk_push_heap_stash(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_push_heap_stash(duk_hthread *thr) { duk_heap *heap; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); heap = thr->heap; DUK_ASSERT(heap->heap_object != NULL); - duk_push_hobject(ctx, heap->heap_object); - duk__push_stash(ctx); + duk_push_hobject(thr, heap->heap_object); + duk__push_stash(thr); } -DUK_EXTERNAL void duk_push_global_stash(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_push_global_object(ctx); - duk__push_stash(ctx); +DUK_EXTERNAL void duk_push_global_stash(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_push_global_object(thr); + duk__push_stash(thr); } -DUK_EXTERNAL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - DUK_ASSERT_CTX_VALID(ctx); - if (!target_ctx) { - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return; /* not reached */ +DUK_EXTERNAL void duk_push_thread_stash(duk_hthread *thr, duk_hthread *target_thr) { + DUK_ASSERT_API_ENTRY(thr); + if (DUK_UNLIKELY(target_thr == NULL)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return;); } - duk_push_hobject(ctx, (duk_hobject *) target_ctx); - duk__push_stash(ctx); + duk_push_hobject(thr, (duk_hobject *) target_thr); + duk__push_stash(thr); } /* XXX: duk_ssize_t would be useful here */ -DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size_t sz, const char *fmt, va_list ap) { +DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_hthread *thr, void *buf, duk_size_t sz, const char *fmt, va_list ap) { duk_int_t len; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(ctx); + DUK_CTX_ASSERT_VALID(thr); + DUK_UNREF(thr); /* NUL terminator handling doesn't matter here */ len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap); @@ -18373,8 +22709,7 @@ DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size return -1; } -DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_push_vsprintf(duk_hthread *thr, const char *fmt, va_list ap) { duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE]; duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE; duk_bool_t pushed_buf = 0; @@ -18382,13 +22717,13 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va duk_int_t len; /* XXX: duk_ssize_t */ const char *res; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* special handling of fmt==NULL */ if (!fmt) { duk_hstring *h_str; - duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); - h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); /* rely on interning, must be this string */ + duk_push_hstring_empty(thr); + h_str = duk_known_hstring(thr, -1); return (const char *) DUK_HSTRING_GET_DATA(h_str); } @@ -18409,14 +22744,14 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va buf = stack_buf; } else if (!pushed_buf) { pushed_buf = 1; - buf = duk_push_dynamic_buffer(ctx, sz); + buf = duk_push_dynamic_buffer(thr, sz); } else { - buf = duk_resize_buffer(ctx, -1, sz); + buf = duk_resize_buffer(thr, -1, sz); } DUK_ASSERT(buf != NULL); DUK_VA_COPY(ap_copy, ap); - len = duk__try_push_vsprintf(ctx, buf, sz, fmt, ap_copy); + len = duk__try_push_vsprintf(thr, buf, sz, fmt, ap_copy); va_end(ap_copy); if (len >= 0) { break; @@ -18424,157 +22759,202 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va /* failed, resize and try again */ sz = sz * 2; - if (sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT) { - DUK_ERROR_API(thr, DUK_STR_SPRINTF_TOO_LONG); + if (DUK_UNLIKELY(sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT)) { + DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); + DUK_WO_NORETURN(return NULL;); } } - /* Cannot use duk_to_string() on the buffer because it is usually - * larger than 'len'. Also, 'buf' is usually a stack buffer. + /* Cannot use duk_buffer_to_string() on the buffer because it is + * usually larger than 'len'; 'buf' is also usually a stack buffer. */ - res = duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); /* [ buf? res ] */ + res = duk_push_lstring(thr, (const char *) buf, (duk_size_t) len); /* [ buf? res ] */ if (pushed_buf) { - duk_remove(ctx, -2); + duk_remove_m2(thr); } return res; } -DUK_EXTERNAL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...) { +DUK_EXTERNAL const char *duk_push_sprintf(duk_hthread *thr, const char *fmt, ...) { va_list ap; const char *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* allow fmt==NULL */ va_start(ap, fmt); - ret = duk_push_vsprintf(ctx, fmt, ap); + ret = duk_push_vsprintf(thr, fmt, ap); va_end(ap); return ret; } -DUK_INTERNAL duk_idx_t duk_push_object_helper(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) { duk_tval *tv_slot; duk_hobject *h; - duk_idx_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(prototype_bidx == -1 || (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS)); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); - h = duk_hobject_alloc(thr->heap, hobject_flags_and_class); - if (!h) { - DUK_ERROR_ALLOC_DEFMSG(thr); - } + h = duk_hobject_alloc(thr, hobject_flags_and_class); + DUK_ASSERT(h != NULL); DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags)); tv_slot = thr->valstack_top; DUK_TVAL_SET_OBJECT(tv_slot, h); DUK_HOBJECT_INCREF(thr, h); /* no side effects */ - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); thr->valstack_top++; /* object is now reachable */ if (prototype_bidx >= 0) { - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[prototype_bidx]); + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, thr->builtins[prototype_bidx]); } else { DUK_ASSERT(prototype_bidx == -1); DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL); } - return ret; + return h; } -DUK_INTERNAL duk_idx_t duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_idx_t ret; +DUK_INTERNAL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto) { duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_push_object_helper(ctx, hobject_flags_and_class, -1); - h = duk_get_hobject(ctx, -1); + h = duk_push_object_helper(thr, hobject_flags_and_class, -1); DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, proto); - return ret; + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, proto); + return h; } -DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_idx_t duk_push_object(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - return duk_push_object_helper(ctx, + (void) duk_push_object_helper(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), DUK_BIDX_OBJECT_PROTOTYPE); + return duk_get_top_index_unsafe(thr); } -DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *obj; +DUK_EXTERNAL duk_idx_t duk_push_array(duk_hthread *thr) { + duk_uint_t flags; + duk_harray *obj; duk_idx_t ret; + duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_ARRAY_PART | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY), - DUK_BIDX_ARRAY_PROTOTYPE); + flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_ARRAY_PART | + DUK_HOBJECT_FLAG_EXOTIC_ARRAY | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY); - obj = duk_require_hobject(ctx, ret); + obj = duk_harray_alloc(thr, flags); + DUK_ASSERT(obj != NULL); - /* - * An array must have a 'length' property (E5 Section 15.4.5.2). - * The special array behavior flag must only be enabled once the - * length property has been added. - * - * The internal property must be a number (and preferably a - * fastint if fastint support is enabled). - */ + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]); - duk_push_int(ctx, 0); -#if defined(DUK_USE_FASTINT) - DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx, -1))); -#endif - - duk_hobject_define_property_internal(thr, - obj, - DUK_HTHREAD_STRING_LENGTH(thr), - DUK_PROPDESC_FLAGS_W); - DUK_HOBJECT_SET_EXOTIC_ARRAY(obj); + tv_slot = thr->valstack_top; + DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); + DUK_HOBJECT_INCREF(thr, obj); /* XXX: could preallocate with refcount = 1 */ + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + thr->valstack_top++; + DUK_ASSERT(obj->length == 0); /* Array .length starts at zero. */ return ret; } -DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_idx_t duk_push_bare_array(duk_hthread *thr) { + duk_uint_t flags; + duk_harray *obj; + duk_idx_t ret; + duk_tval *tv_slot; + + DUK_ASSERT_API_ENTRY(thr); + + flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_ARRAY_PART | + DUK_HOBJECT_FLAG_EXOTIC_ARRAY | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY); + + obj = duk_harray_alloc(thr, flags); + DUK_ASSERT(obj != NULL); + + tv_slot = thr->valstack_top; + DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); + DUK_HOBJECT_INCREF(thr, obj); /* XXX: could preallocate with refcount = 1 */ + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + thr->valstack_top++; + + DUK_ASSERT(obj->length == 0); /* Array .length starts at zero. */ + return ret; +} + +DUK_INTERNAL duk_harray *duk_push_harray(duk_hthread *thr) { + /* XXX: API call could do this directly, cast to void in API macro. */ + duk_harray *a; + + DUK_ASSERT_API_ENTRY(thr); + + (void) duk_push_array(thr); + DUK_ASSERT(DUK_TVAL_IS_OBJECT(thr->valstack_top - 1)); + a = (duk_harray *) DUK_TVAL_GET_OBJECT(thr->valstack_top - 1); + DUK_ASSERT(a != NULL); + return a; +} + +/* Push a duk_harray with preallocated size (.length also set to match size). + * Caller may then populate array part of the duk_harray directly. + */ +DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size) { + duk_harray *a; + + DUK_ASSERT_API_ENTRY(thr); + + a = duk_push_harray(thr); + + duk_hobject_realloc_props(thr, + (duk_hobject *) a, + 0, + size, + 0, + 0); + a->length = size; + return a; +} + +DUK_INTERNAL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size) { + duk_harray *a; + + DUK_ASSERT_API_ENTRY(thr); + + a = duk_push_harray_with_size(thr, size); + DUK_ASSERT(a != NULL); + return DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a); +} + +DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_hthread *thr, duk_uint_t flags) { duk_hthread *obj; duk_idx_t ret; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); - obj = duk_hthread_alloc(thr->heap, + obj = duk_hthread_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_THREAD | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); - if (!obj) { - DUK_ERROR_ALLOC_DEFMSG(thr); - } + DUK_ASSERT(obj != NULL); obj->state = DUK_HTHREAD_STATE_INACTIVE; #if defined(DUK_USE_ROM_STRINGS) /* Nothing to initialize, strs[] is in ROM. */ @@ -18595,8 +22975,9 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { thr->valstack_top++; /* important to do this *after* pushing, to make the thread reachable for gc */ - if (!duk_hthread_init_stacks(thr->heap, obj)) { - DUK_ERROR_ALLOC_DEFMSG(thr); + if (DUK_UNLIKELY(!duk_hthread_init_stacks(thr->heap, obj))) { + DUK_ERROR_ALLOC_FAILED(thr); + DUK_WO_NORETURN(return 0;); } /* initialize built-ins - either by copying or creating new ones */ @@ -18606,10 +22987,10 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { duk_hthread_copy_builtin_objects(thr, obj); } - /* default prototype (Note: 'obj' must be reachable) */ - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]); + /* default prototype */ + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]); - /* Initial stack size satisfies the stack spare constraints so there + /* Initial stack size satisfies the stack slack constraints so there * is no need to require stack here. */ DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >= @@ -18618,30 +22999,27 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { return ret; } -DUK_INTERNAL duk_idx_t duk_push_compiledfunction(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hcompiledfunction *obj; - duk_idx_t ret; +DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr) { + duk_hcompfunc *obj; duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); /* Template functions are not strictly constructable (they don't * have a "prototype" property for instance), so leave the * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here. */ - obj = duk_hcompiledfunction_alloc(thr->heap, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_COMPILEDFUNCTION | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); - if (!obj) { - DUK_ERROR_ALLOC_DEFMSG(thr); + obj = duk_hcompfunc_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_FLAG_COMPFUNC | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); + if (DUK_UNLIKELY(obj == NULL)) { + DUK_ERROR_ALLOC_FAILED(thr); + DUK_WO_NORETURN(return NULL;); } DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags)); @@ -18649,43 +23027,68 @@ DUK_INTERNAL duk_idx_t duk_push_compiledfunction(duk_context *ctx) { tv_slot = thr->valstack_top; DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); DUK_HOBJECT_INCREF(thr, obj); - ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); thr->valstack_top++; - /* default prototype (Note: 'obj' must be reachable) */ - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); + /* default prototype */ + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL); + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - return ret; + return obj; } -DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hnativefunction *obj; +DUK_INTERNAL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr) { + duk_hboundfunc *obj; + duk_tval *tv_slot; + + DUK_ASSERT_API_ENTRY(thr); + + DUK__CHECK_SPACE(); + obj = duk_hboundfunc_alloc(thr->heap, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BOUNDFUNC | + DUK_HOBJECT_FLAG_CONSTRUCTABLE | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); + if (!obj) { + DUK_ERROR_ALLOC_FAILED(thr); + DUK_WO_NORETURN(return NULL;); + } + + tv_slot = thr->valstack_top++; + DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); + DUK_HOBJECT_INCREF(thr, obj); + + /* Prototype is left as NULL because the caller always sets it (and + * it depends on the target function). + */ + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL); + + return obj; +} + +DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx) { + duk_hnatfunc *obj; duk_idx_t ret; duk_tval *tv_slot; duk_int16_t func_nargs; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } - if (func == NULL) { + DUK__CHECK_SPACE(); + + if (DUK_UNLIKELY(func == NULL)) { goto api_error; } - if (nargs >= 0 && nargs < DUK_HNATIVEFUNCTION_NARGS_MAX) { + if (nargs >= 0 && nargs < DUK_HNATFUNC_NARGS_MAX) { func_nargs = (duk_int16_t) nargs; } else if (nargs == DUK_VARARGS) { - func_nargs = DUK_HNATIVEFUNCTION_NARGS_VARARGS; + func_nargs = DUK_HNATFUNC_NARGS_VARARGS; } else { goto api_error; } - obj = duk_hnativefunction_alloc(thr->heap, flags); - if (!obj) { - DUK_ERROR_ALLOC_DEFMSG(thr); - } + obj = duk_hnatfunc_alloc(thr, flags); + DUK_ASSERT(obj != NULL); obj->func = func; obj->nargs = func_nargs; @@ -18699,75 +23102,80 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); thr->valstack_top++; - /* default prototype (Note: 'obj' must be reachable) */ - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]); - + DUK_ASSERT_BIDX_VALID(proto_bidx); + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[proto_bidx]); return ret; api_error: - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return 0; /* not reached */ + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); } -DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_int_t nargs) { +DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { duk_uint_t flags; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | - DUK_HOBJECT_FLAG_NATIVEFUNCTION | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | DUK_HOBJECT_FLAG_NOTAIL | - DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - return duk__push_c_function_raw(ctx, func, nargs, flags); + /* Default prototype is a Duktape specific %NativeFunctionPrototype% + * which provides .length and .name getters. + */ + return duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE); } -DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) { +DUK_INTERNAL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { duk_uint_t flags; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | - DUK_HOBJECT_FLAG_NATIVEFUNCTION | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, nargs, flags); + /* Must use Function.prototype for standard built-in functions. */ + (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE); } -DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) { +DUK_INTERNAL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs) { duk_uint_t flags; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_NATIVEFUNCTION | + DUK_HOBJECT_FLAG_CALLABLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, nargs, flags); + /* Must use Function.prototype for standard built-in functions. */ + (void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE); } -DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval tv_tmp; +DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) { duk_small_uint_t lf_flags; + duk_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) { /* as is */ @@ -18776,44 +23184,40 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun } else { goto api_error; } - if (!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX)) { + if (DUK_UNLIKELY(!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX))) { goto api_error; } - if (!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX)) { + if (DUK_UNLIKELY(!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX))) { goto api_error; } - lf_flags = DUK_LFUNC_FLAGS_PACK(magic, length, nargs); - DUK_TVAL_SET_LIGHTFUNC(&tv_tmp, func, lf_flags); - duk_push_tval(ctx, &tv_tmp); /* XXX: direct valstack write */ - DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); - return ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1; + lf_flags = DUK_LFUNC_FLAGS_PACK((duk_small_int_t) magic, (duk_small_uint_t) length, (duk_small_uint_t) nargs); + tv_slot = thr->valstack_top++; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_slot)); + DUK_TVAL_SET_LIGHTFUNC(tv_slot, func, lf_flags); + DUK_ASSERT(tv_slot >= thr->valstack_bottom); + return (duk_idx_t) (tv_slot - thr->valstack_bottom); api_error: - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); - return 0; /* not reached */ + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); } -DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_hbufferobject *obj; +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) { + duk_hbufobj *obj; duk_tval *tv_slot; - DUK_ASSERT(ctx != NULL); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(prototype_bidx >= 0); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); - obj = duk_hbufferobject_alloc(thr->heap, hobject_flags_and_class); - if (!obj) { - DUK_ERROR_ALLOC_DEFMSG(thr); - } + obj = duk_hbufobj_alloc(thr, hobject_flags_and_class); + DUK_ASSERT(obj != NULL); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]); - DUK_ASSERT_HBUFFEROBJECT_VALID(obj); + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]); + DUK_HBUFOBJ_ASSERT_VALID(obj); tv_slot = thr->valstack_top; DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj); @@ -18822,238 +23226,253 @@ DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_ return obj; } +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* XXX: There's quite a bit of overlap with buffer creation handling in * duk_bi_buffer.c. Look for overlap and refactor. */ -#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \ - (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview)) - #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,istypedarray) \ + (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (istypedarray)) + static const duk_uint32_t duk__bufobj_flags_lookup[] = { - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_DATAVIEW */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, DUK_BIDX_INT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT8, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT16, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT16, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT32, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, DUK_BIDX_UINT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT32, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT32, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */ - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT64, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */ -}; -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* Only allow Duktape.Buffer when support disabled. */ -static const duk_uint32_t duk__bufobj_flags_lookup[] = { - DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0) /* DUK_BUFOBJ_DUKTAPE_BUFFER */ + /* Node.js Buffers are Uint8Array instances which inherit from Buffer.prototype. */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_NODEJS_BUFFER */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_DATAVIEW */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, DUK_BIDX_INT8ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_INT8, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT8CLAMPED, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_INT16, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT16, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_INT32, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, DUK_BIDX_UINT32ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_UINT32, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_FLOAT32, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */ + DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, DUK_HBUFOBJ_ELEM_FLOAT64, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */ }; #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#undef DUK__PACK_ARGS -DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) { - duk_hthread *thr; - duk_hbufferobject *h_bufobj; +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) { + duk_hbufobj *h_bufobj; duk_hbuffer *h_val; + duk_hobject *h_arraybuf; duk_uint32_t tmp; duk_uint_t classnum; duk_uint_t protobidx; duk_uint_t lookupidx; duk_uint_t uint_offset, uint_length, uint_added; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - /* The underlying types for offset/length in duk_hbufferobject is - * duk_uint_t; make sure argument values fit and that offset + length - * does not wrap. + /* The underlying types for offset/length in duk_hbufobj is + * duk_uint_t; make sure argument values fit. */ uint_offset = (duk_uint_t) byte_offset; uint_length = (duk_uint_t) byte_length; if (sizeof(duk_size_t) != sizeof(duk_uint_t)) { - if ((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length) { + if (DUK_UNLIKELY((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length)) { goto range_error; } } - uint_added = uint_offset + uint_length; - if (uint_added < uint_offset) { - goto range_error; - } - DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length); DUK_ASSERT_DISABLE(flags >= 0); /* flags is unsigned */ - lookupidx = flags & 0x0f; /* 4 low bits */ - if (lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t)) { + lookupidx = flags; + if (DUK_UNLIKELY(lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t))) { goto arg_error; } tmp = duk__bufobj_flags_lookup[lookupidx]; classnum = tmp >> 24; protobidx = (tmp >> 16) & 0xff; - h_val = duk_require_hbuffer(ctx, idx_buffer); + h_arraybuf = duk_get_hobject(thr, idx_buffer); + if (h_arraybuf != NULL && /* argument is an object */ + flags != DUK_BUFOBJ_ARRAYBUFFER && /* creating a view */ + DUK_HOBJECT_GET_CLASS_NUMBER(h_arraybuf) == DUK_HOBJECT_CLASS_ARRAYBUFFER /* argument is ArrayBuffer */) { + duk_uint_t tmp_offset; + + DUK_HBUFOBJ_ASSERT_VALID((duk_hbufobj *) h_arraybuf); + h_val = ((duk_hbufobj *) h_arraybuf)->buf; + if (DUK_UNLIKELY(h_val == NULL)) { + goto arg_error; + } + + tmp_offset = uint_offset + ((duk_hbufobj *) h_arraybuf)->offset; + if (DUK_UNLIKELY(tmp_offset < uint_offset)) { + goto range_error; + } + uint_offset = tmp_offset; + + /* Note intentional difference to new TypedArray(): we allow + * caller to create an uncovered typed array (which is memory + * safe); new TypedArray() rejects it. + */ + } else { + /* Handle unexpected object arguments here too, for nice error + * messages. + */ + h_arraybuf = NULL; + h_val = duk_require_hbuffer(thr, idx_buffer); + } + + /* Wrap check for offset+length. */ + uint_added = uint_offset + uint_length; + if (DUK_UNLIKELY(uint_added < uint_offset)) { + goto range_error; + } + DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length); + DUK_ASSERT(h_val != NULL); - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(classnum), - protobidx); + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(classnum), + (duk_small_int_t) protobidx); DUK_ASSERT(h_bufobj != NULL); h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); + h_bufobj->buf_prop = h_arraybuf; + DUK_HOBJECT_INCREF_ALLOWNULL(thr, h_arraybuf); h_bufobj->offset = uint_offset; h_bufobj->length = uint_length; h_bufobj->shift = (tmp >> 4) & 0x0f; h_bufobj->elem_type = (tmp >> 8) & 0xff; - h_bufobj->is_view = tmp & 0x0f; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + h_bufobj->is_typedarray = tmp & 0x0f; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* TypedArray views need an automatic ArrayBuffer which must be - * provided as .buffer property of the view. Just create a new - * ArrayBuffer sharing the same underlying buffer. + * provided as .buffer property of the view. The ArrayBuffer is + * referenced via duk_hbufobj->buf_prop and an inherited .buffer + * accessor returns it. The ArrayBuffer is created lazily on first + * access if necessary so we don't need to do anything more here. */ - if (flags & DUK_BUFOBJ_CREATE_ARRBUF) { - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - - DUK_ASSERT(h_bufobj != NULL); - - h_bufobj->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->offset = uint_offset; - h_bufobj->length = uint_length; - DUK_ASSERT(h_bufobj->shift == 0); - h_bufobj->elem_type = DUK_HBUFFEROBJECT_ELEM_UINT8; - DUK_ASSERT(h_bufobj->is_view == 0); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE); - duk_compact(ctx, -1); - } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - return; range_error: - DUK_ERROR_RANGE(thr, DUK_STR_INVALID_CALL_ARGS); - return; /* not reached */ + DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS); + DUK_WO_NORETURN(return;); arg_error: - DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CALL_ARGS); - return; /* not reached */ + DUK_ERROR_TYPE(thr, DUK_STR_INVALID_ARGS); + DUK_WO_NORETURN(return;); } +#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ +DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(idx_buffer); + DUK_UNREF(byte_offset); + DUK_UNREF(byte_length); + DUK_UNREF(flags); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_idx_t ret; +DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) { duk_hobject *proto; -#ifdef DUK_USE_AUGMENT_ERROR_CREATE - duk_bool_t noblame_fileline; +#if defined(DUK_USE_AUGMENT_ERROR_CREATE) + duk_small_uint_t augment_flags; #endif - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); DUK_UNREF(filename); DUK_UNREF(line); /* Error code also packs a tracedata related flag. */ -#ifdef DUK_USE_AUGMENT_ERROR_CREATE - noblame_fileline = err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE; +#if defined(DUK_USE_AUGMENT_ERROR_CREATE) + augment_flags = 0; + if (err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE) { + augment_flags = DUK_AUGMENT_FLAG_NOBLAME_FILELINE; + } #endif err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE); /* error gets its 'name' from the prototype */ proto = duk_error_prototype_from_code(thr, err_code); - ret = duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR), - proto); + (void) duk_push_object_helper_proto(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR), + proto); /* ... and its 'message' from an instance property */ if (fmt) { - duk_push_vsprintf(ctx, fmt, ap); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); + duk_push_vsprintf(thr, fmt, ap); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); } else { /* If no explicit message given, put error code into message field - * (as a number). This is not fully in keeping with the Ecmascript + * (as a number). This is not fully in keeping with the ECMAScript * error model because messages are supposed to be strings (Error * constructors use ToString() on their argument). However, it's * probably more useful than having a separate 'code' property. */ - duk_push_int(ctx, err_code); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); + duk_push_int(thr, err_code); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); } /* XXX: .code = err_code disabled, not sure if useful */ /* Creation time error augmentation */ -#ifdef DUK_USE_AUGMENT_ERROR_CREATE +#if defined(DUK_USE_AUGMENT_ERROR_CREATE) /* filename may be NULL in which case file/line is not recorded */ - duk_err_augment_error_create(thr, thr, filename, line, noblame_fileline); /* may throw an error */ + duk_err_augment_error_create(thr, thr, filename, line, augment_flags); /* may throw an error */ #endif - return ret; + return duk_get_top_index_unsafe(thr); } -DUK_EXTERNAL duk_idx_t duk_push_error_object_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) { +DUK_EXTERNAL duk_idx_t duk_push_error_object_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) { va_list ap; duk_idx_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); va_start(ap, fmt); - ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap); + ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); va_end(ap); return ret; } #if !defined(DUK_USE_VARIADIC_MACROS) -DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) { +DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) { const char *filename = duk_api_global_filename; duk_int_t line = duk_api_global_line; va_list ap; duk_idx_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); duk_api_global_filename = NULL; duk_api_global_line = 0; va_start(ap, fmt); - ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap); + ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); va_end(ap); return ret; } #endif /* DUK_USE_VARIADIC_MACROS */ -DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void *duk_push_buffer_raw(duk_hthread *thr, duk_size_t size, duk_small_uint_t flags) { duk_tval *tv_slot; duk_hbuffer *h; void *buf_data; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); - } + DUK__CHECK_SPACE(); /* Check for maximum buffer length. */ - if (size > DUK_HBUFFER_MAX_BYTELEN) { + if (DUK_UNLIKELY(size > DUK_HBUFFER_MAX_BYTELEN)) { DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); + DUK_WO_NORETURN(return NULL;); } h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data); - if (!h) { - DUK_ERROR_ALLOC_DEFMSG(thr); + if (DUK_UNLIKELY(h == NULL)) { + DUK_ERROR_ALLOC_FAILED(thr); + DUK_WO_NORETURN(return NULL;); } tv_slot = thr->valstack_top; @@ -19064,124 +23483,404 @@ DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_sm return (void *) buf_data; } -DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_idx_t ret; +DUK_INTERNAL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len) { + DUK_ASSERT_API_ENTRY(thr); + return duk_push_buffer_raw(thr, len, DUK_BUF_FLAG_NOZERO); +} - DUK_ASSERT_CTX_VALID(ctx); +DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len) { + void *ptr; + + DUK_ASSERT_API_ENTRY(thr); + + ptr = duk_push_buffer_raw(thr, len, 0); + DUK_ASSERT(ptr != NULL); +#if !defined(DUK_USE_ZERO_BUFFER_DATA) + /* ES2015 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA + * is not set. + */ + duk_memzero((void *) ptr, (size_t) len); +#endif + return ptr; +} + +#if defined(DUK_USE_ES6_PROXY) +DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) { + duk_hobject *h_target; + duk_hobject *h_handler; + duk_hproxy *h_proxy; + duk_tval *tv_slot; + duk_uint_t flags; + + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(proxy_flags); + + /* DUK__CHECK_SPACE() unnecessary because the Proxy is written to + * value stack in-place. + */ +#if 0 + DUK__CHECK_SPACE(); +#endif + + /* Reject a proxy object as the target because it would need + * special handling in property lookups. (ES2015 has no such + * restriction.) + */ + h_target = duk_require_hobject_promote_mask(thr, -2, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); + DUK_ASSERT(h_target != NULL); + if (DUK_HOBJECT_IS_PROXY(h_target)) { + goto fail_args; + } + + /* Reject a proxy object as the handler because it would cause + * potentially unbounded recursion. (ES2015 has no such + * restriction.) + * + * There's little practical reason to use a lightfunc or a plain + * buffer as the handler table: one could only provide traps via + * their prototype objects (Function.prototype and ArrayBuffer.prototype). + * Even so, as lightfuncs and plain buffers mimic their object + * counterparts, they're promoted and accepted here. + */ + h_handler = duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); + DUK_ASSERT(h_handler != NULL); + if (DUK_HOBJECT_IS_PROXY(h_handler)) { + goto fail_args; + } + + /* XXX: Proxy object currently has no prototype, so ToPrimitive() + * coercion fails which is a bit confusing. + */ + + /* CALLABLE and CONSTRUCTABLE flags are copied from the (initial) + * target, see ES2015 Sections 9.5.15 and 9.5.13. + */ + flags = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h_target) & + (DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE); + flags |= DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ; + if (flags & DUK_HOBJECT_FLAG_CALLABLE) { + flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION) | + DUK_HOBJECT_FLAG_SPECIAL_CALL; + } else { + flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT); + } + + h_proxy = duk_hproxy_alloc(thr, flags); + DUK_ASSERT(h_proxy != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_proxy) == NULL); + + /* Initialize Proxy target and handler references; avoid INCREF + * by stealing the value stack refcounts via direct value stack + * manipulation. INCREF is needed for the Proxy itself however. + */ + DUK_ASSERT(h_target != NULL); + h_proxy->target = h_target; + DUK_ASSERT(h_handler != NULL); + h_proxy->handler = h_handler; + DUK_HPROXY_ASSERT_VALID(h_proxy); + + DUK_ASSERT(duk_get_hobject(thr, -2) == h_target); + DUK_ASSERT(duk_get_hobject(thr, -1) == h_handler); + tv_slot = thr->valstack_top - 2; + DUK_ASSERT(tv_slot >= thr->valstack_bottom); + DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) h_proxy); + DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_proxy); + tv_slot++; + DUK_TVAL_SET_UNDEFINED(tv_slot); /* [ ... target handler ] -> [ ... proxy undefined ] */ + thr->valstack_top = tv_slot; /* -> [ ... proxy ] */ + + DUK_DD(DUK_DDPRINT("created Proxy: %!iT", duk_get_tval(thr, -1))); + + return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom - 1); + + fail_args: + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); +} +#else /* DUK_USE_ES6_PROXY */ +DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(proxy_flags); + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return 0;); +} +#endif /* DUK_USE_ES6_PROXY */ + +#if defined(DUK_USE_ASSERTIONS) +DUK_LOCAL void duk__validate_push_heapptr(duk_hthread *thr, void *ptr) { + duk_heaphdr *h; + duk_heaphdr *curr; + duk_bool_t found = 0; + + h = (duk_heaphdr *) ptr; + if (h == NULL) { + /* Allowed. */ + return; + } + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + + /* One particular problem case is where an object has been + * queued for finalization but the finalizer hasn't yet been + * executed. + * + * Corner case: we're running in a finalizer for object X, and + * user code calls duk_push_heapptr() for X itself. In this + * case X will be in finalize_list, and we can detect the case + * by seeing that X's FINALIZED flag is set (which is done before + * the finalizer starts executing). + */ +#if defined(DUK_USE_FINALIZER_SUPPORT) + for (curr = thr->heap->finalize_list; + curr != NULL; + curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { + /* FINALIZABLE is set for all objects on finalize_list + * except for an object being finalized right now. So + * can't assert here. + */ +#if 0 + DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr)); +#endif + + if (curr == h) { + if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) { + /* Object is currently being finalized. */ + DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ + found = 1; + } else { + /* Not being finalized but on finalize_list, + * allowed since Duktape 2.1. + */ + DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ + found = 1; + } + } + } +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +#if defined(DUK_USE_REFERENCE_COUNTING) + /* Because refzero_list is now processed to completion inline with + * no side effects, it's always empty here. + */ + DUK_ASSERT(thr->heap->refzero_list == NULL); +#endif + + /* If not present in finalize_list (or refzero_list), it + * must be either in heap_allocated or the string table. + */ + if (DUK_HEAPHDR_IS_STRING(h)) { + duk_uint32_t i; + duk_hstring *str; + duk_heap *heap = thr->heap; + + DUK_ASSERT(found == 0); + for (i = 0; i < heap->st_size; i++) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + str = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]); +#else + str = heap->strtable[i]; +#endif + while (str != NULL) { + if (str == (duk_hstring *) h) { + DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ + found = 1; + break; + } + str = str->hdr.h_next; + } + } + DUK_ASSERT(found != 0); + } else { + for (curr = thr->heap->heap_allocated; + curr != NULL; + curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { + if (curr == h) { + DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ + found = 1; + } + } + DUK_ASSERT(found != 0); + } +} +#endif /* DUK_USE_ASSERTIONS */ + +DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_hthread *thr, void *ptr) { + duk_idx_t ret; + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + /* Reviving an object using a heap pointer is a dangerous API + * operation: if the application doesn't guarantee that the + * pointer target is always reachable, difficult-to-diagnose + * problems may ensue. Try to validate the 'ptr' argument to + * the extent possible. + */ + +#if defined(DUK_USE_ASSERTIONS) + duk__validate_push_heapptr(thr, ptr); +#endif + + DUK__CHECK_SPACE(); ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + tv = thr->valstack_top++; if (ptr == NULL) { - goto push_undefined; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); + return ret; } - switch ((int) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) { + DUK_HEAPHDR_ASSERT_VALID((duk_heaphdr *) ptr); + + /* If the argument is on finalize_list it has technically been + * unreachable before duk_push_heapptr() but it's still safe to + * push it. Starting from Duktape 2.1 allow application code to + * do so. There are two main cases: + * + * (1) The object is on the finalize_list and we're called by + * the finalizer for the object being finalized. In this + * case do nothing: finalize_list handling will deal with + * the object queueing. This is detected by the object not + * having a FINALIZABLE flag despite being on the finalize_list; + * the flag is cleared for the object being finalized only. + * + * (2) The object is on the finalize_list but is not currently + * being processed. In this case the object can be queued + * back to heap_allocated with a few flags cleared, in effect + * cancelling the finalizer. + */ + if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) ptr))) { + duk_heaphdr *curr; + + DUK_D(DUK_DPRINT("duk_push_heapptr() with a pointer on finalize_list, autorescue")); + + curr = (duk_heaphdr *) ptr; + DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); + + /* Because FINALIZED is set prior to finalizer call, it will + * be set for the object being currently finalized, but not + * for other objects on finalize_list. + */ + DUK_HEAPHDR_CLEAR_FINALIZED(curr); + + /* Dequeue object from finalize_list and queue it back to + * heap_allocated. + */ +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); /* Preincremented on finalize_list insert. */ + DUK_HEAPHDR_PREDEC_REFCOUNT(curr); +#endif + DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(thr->heap, curr); + DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(thr->heap, curr); + + /* Continue with the rest. */ + } + + switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) { case DUK_HTYPE_STRING: - duk_push_hstring(ctx, (duk_hstring *) ptr); + DUK_TVAL_SET_STRING(tv, (duk_hstring *) ptr); break; case DUK_HTYPE_OBJECT: - duk_push_hobject(ctx, (duk_hobject *) ptr); - break; - case DUK_HTYPE_BUFFER: - duk_push_hbuffer(ctx, (duk_hbuffer *) ptr); + DUK_TVAL_SET_OBJECT(tv, (duk_hobject *) ptr); break; default: - goto push_undefined; + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr) == DUK_HTYPE_BUFFER); + DUK_TVAL_SET_BUFFER(tv, (duk_hbuffer *) ptr); + break; } - return ret; - push_undefined: - duk_push_undefined(ctx); + DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) ptr); + return ret; } -DUK_INTERNAL duk_idx_t duk_push_object_internal(duk_context *ctx) { - return duk_push_object_helper(ctx, +/* Push object with no prototype, i.e. a "bare" object. */ +DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + + (void) duk_push_object_helper(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), -1); /* no prototype */ + return duk_get_top_index_unsafe(thr); } -DUK_INTERNAL void duk_push_hstring(duk_context *ctx, duk_hstring *h) { +DUK_INTERNAL void duk_push_hstring(duk_hthread *thr, duk_hstring *h) { duk_tval tv; - DUK_ASSERT_CTX_VALID(ctx); + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(h != NULL); + DUK_TVAL_SET_STRING(&tv, h); - duk_push_tval(ctx, &tv); + duk_push_tval(thr, &tv); } -DUK_INTERNAL void duk_push_hstring_stridx(duk_context *ctx, duk_small_int_t stridx) { - duk_hthread *thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS); - duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); +DUK_INTERNAL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT_STRIDX_VALID(stridx); + duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx)); } -DUK_INTERNAL void duk_push_hobject(duk_context *ctx, duk_hobject *h) { +DUK_INTERNAL void duk_push_hstring_empty(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, DUK_STRIDX_EMPTY_STRING)); +} + +DUK_INTERNAL void duk_push_hobject(duk_hthread *thr, duk_hobject *h) { duk_tval tv; - DUK_ASSERT_CTX_VALID(ctx); + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(h != NULL); + DUK_TVAL_SET_OBJECT(&tv, h); - duk_push_tval(ctx, &tv); + duk_push_tval(thr, &tv); } -DUK_INTERNAL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h) { +DUK_INTERNAL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h) { duk_tval tv; - DUK_ASSERT_CTX_VALID(ctx); + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(h != NULL); + DUK_TVAL_SET_BUFFER(&tv, h); - duk_push_tval(ctx, &tv); + duk_push_tval(thr, &tv); } -DUK_INTERNAL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx) { - duk_hthread *thr = (duk_hthread *) ctx; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); +DUK_INTERNAL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS); DUK_ASSERT(thr->builtins[builtin_idx] != NULL); - duk_push_hobject(ctx, thr->builtins[builtin_idx]); + + duk_push_hobject(thr, thr->builtins[builtin_idx]); } /* * Poppers */ -DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_n_unsafe_raw(duk_hthread *thr, duk_idx_t count) { duk_tval *tv; +#if defined(DUK_USE_REFERENCE_COUNTING) + duk_tval *tv_end; +#endif - DUK_ASSERT_CTX_VALID(ctx); - - if (DUK_UNLIKELY(count < 0)) { - DUK_ERROR_API(thr, DUK_STR_INVALID_COUNT); - return; - } - - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - if (DUK_UNLIKELY((duk_size_t) (thr->valstack_top - thr->valstack_bottom) < (duk_size_t) count)) { - DUK_ERROR_API(thr, DUK_STR_POP_TOO_MANY); - } - - /* - * Must be very careful here, every DECREF may cause reallocation - * of our valstack. - */ - - /* XXX: inlined DECREF macro would be nice here: no NULL check, - * refzero queueing but no refzero algorithm run (= no pointer - * instability), inline code. - */ - - /* XXX: optimize loops */ + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); #if defined(DUK_USE_REFERENCE_COUNTING) - while (count > 0) { - count--; - tv = --thr->valstack_top; /* tv points to element just below prev top */ + tv = thr->valstack_top; + tv_end = tv - count; + while (tv != tv_end) { + tv--; DUK_ASSERT(tv >= thr->valstack_bottom); - DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ + DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); } + thr->valstack_top = tv; + DUK_REFZERO_CHECK_FAST(thr); #else tv = thr->valstack_top; while (count > 0) { @@ -19196,59 +23895,414 @@ DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) { DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); } +DUK_EXTERNAL void duk_pop_n(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + + if (DUK_UNLIKELY((duk_uidx_t) (thr->valstack_top - thr->valstack_bottom) < (duk_uidx_t) count)) { + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return;); + } + DUK_ASSERT(count >= 0); + + duk__pop_n_unsafe_raw(thr, count); +} + +#if defined(DUK_USE_PREFER_SIZE) +DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n(thr, count); +} +#else /* DUK_USE_PREFER_SIZE */ +DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + duk__pop_n_unsafe_raw(thr, count); +} +#endif /* DUK_USE_PREFER_SIZE */ + +/* Pop N elements without DECREF (in effect "stealing" any actual refcounts). */ +#if defined(DUK_USE_REFERENCE_COUNTING) +DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); + + tv = thr->valstack_top; + while (count > 0) { + count--; + tv--; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED(tv); + } + thr->valstack_top = tv; + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} +#else /* DUK_USE_REFERENCE_COUNTING */ +DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_unsafe(thr, count); +} +#endif /* DUK_USE_REFERENCE_COUNTING */ + /* Popping one element is called so often that when footprint is not an issue, * compile a specialized function for it. */ #if defined(DUK_USE_PREFER_SIZE) -DUK_EXTERNAL void duk_pop(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 1); +DUK_EXTERNAL void duk_pop(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n(thr, 1); } -#else -DUK_EXTERNAL void duk_pop(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_unsafe(thr, 1); +} +DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_nodecref_unsafe(thr, 1); +} +#else /* DUK_USE_PREFER_SIZE */ +DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_unsafe_raw(duk_hthread *thr) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { - DUK_ERROR_API(thr, DUK_STR_POP_TOO_MANY); - } + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); - tv = --thr->valstack_top; /* tv points to element just below prev top */ + tv = --thr->valstack_top; DUK_ASSERT(tv >= thr->valstack_bottom); -#ifdef DUK_USE_REFERENCE_COUNTING +#if defined(DUK_USE_REFERENCE_COUNTING) DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ #else DUK_TVAL_SET_UNDEFINED(tv); #endif + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} +DUK_EXTERNAL void duk_pop(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return;); + } + + duk__pop_unsafe_raw(thr); +} +DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk__pop_unsafe_raw(thr); +} +DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); + + tv = --thr->valstack_top; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED(tv); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); } #endif /* !DUK_USE_PREFER_SIZE */ -DUK_EXTERNAL void duk_pop_2(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 2); +#if defined(DUK_USE_PREFER_SIZE) +DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_nodecref_unsafe(thr); +} +#else /* DUK_USE_PREFER_SIZE */ +DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); + + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); + thr->valstack_top--; + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} +#endif /* !DUK_USE_PREFER_SIZE */ + +#if defined(DUK_USE_PREFER_SIZE) +DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n(thr, 2); +} +DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_unsafe(thr, 2); +} +DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_nodecref_unsafe(thr, 2); +} +#else +DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_2_unsafe_raw(duk_hthread *thr) { + duk_tval *tv; + + DUK_CTX_ASSERT_VALID(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2); + + tv = --thr->valstack_top; + DUK_ASSERT(tv >= thr->valstack_bottom); +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ +#else + DUK_TVAL_SET_UNDEFINED(tv); +#endif + tv = --thr->valstack_top; + DUK_ASSERT(tv >= thr->valstack_bottom); +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */ +#else + DUK_TVAL_SET_UNDEFINED(tv); +#endif + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} +DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + if (DUK_UNLIKELY(thr->valstack_top - 2 < thr->valstack_bottom)) { + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return;); + } + + duk__pop_2_unsafe_raw(thr); +} +DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk__pop_2_unsafe_raw(thr); +} +DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2); + + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 2)); + thr->valstack_top -= 2; + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} +#endif /* !DUK_USE_PREFER_SIZE */ + +DUK_EXTERNAL void duk_pop_3(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n(thr, 3); } -DUK_EXTERNAL void duk_pop_3(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 3); +DUK_INTERNAL void duk_pop_3_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_unsafe(thr, 3); +} + +DUK_INTERNAL void duk_pop_3_nodecref_unsafe(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n_nodecref_unsafe(thr, 3); +} + +/* + * Pack and unpack (pack value stack entries into an array and vice versa) + */ + +/* XXX: pack index range? array index offset? */ +/* XXX: need ability to pack into a bare array? */ +DUK_INTERNAL void duk_pack(duk_hthread *thr, duk_idx_t count) { + duk_tval *tv_src; + duk_tval *tv_dst; + duk_tval *tv_curr; + duk_tval *tv_limit; + duk_idx_t top; + + DUK_ASSERT_API_ENTRY(thr); + + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + DUK_ASSERT(top >= 0); + if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) top)) { + /* Also handles negative count. */ + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return;); + } + DUK_ASSERT(count >= 0); + + /* Wrapping is controlled by the check above: value stack top can be + * at most DUK_USE_VALSTACK_LIMIT which is low enough so that + * multiplying with sizeof(duk_tval) won't wrap. + */ + DUK_ASSERT(count >= 0 && count <= (duk_idx_t) DUK_USE_VALSTACK_LIMIT); + DUK_ASSERT((duk_size_t) count <= DUK_SIZE_MAX / sizeof(duk_tval)); /* no wrapping */ + + tv_dst = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count); /* XXX: uninitialized would be OK */ + DUK_ASSERT(count == 0 || tv_dst != NULL); + DUK_ASSERT(!duk_is_bare_object(thr, -1)); + + /* Copy value stack values directly to the array part without + * any refcount updates: net refcount changes are zero. + */ + tv_src = thr->valstack_top - count - 1; + duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval)); + + /* Overwrite result array to final value stack location and wipe + * the rest; no refcount operations needed. + */ + + tv_dst = tv_src; /* when count == 0, same as tv_src (OK) */ + tv_src = thr->valstack_top - 1; + DUK_TVAL_SET_TVAL(tv_dst, tv_src); + + /* XXX: internal helper to wipe a value stack segment? */ + tv_curr = tv_dst + 1; + tv_limit = thr->valstack_top; + while (tv_curr != tv_limit) { + /* Wipe policy: keep as 'undefined'. */ + DUK_TVAL_SET_UNDEFINED(tv_curr); + tv_curr++; + } + thr->valstack_top = tv_dst + 1; +} + +DUK_INTERNAL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + + tv = duk_require_tval(thr, idx); + if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv))) { + duk_hobject *h; + duk_uint32_t len; + duk_uint32_t i; + + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + DUK_UNREF(h); + +#if defined(DUK_USE_ARRAY_FASTPATH) /* close enough */ + if (DUK_LIKELY(DUK_HOBJECT_IS_ARRAY(h) && + ((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h))) { + duk_harray *h_arr; + duk_tval *tv_src; + duk_tval *tv_dst; + + h_arr = (duk_harray *) h; + len = h_arr->length; + if (DUK_UNLIKELY(len >= 0x80000000UL)) { + goto fail_over_2g; + } + duk_require_stack(thr, (duk_idx_t) len); + + /* The potential allocation in duk_require_stack() may + * run a finalizer which modifies the argArray so that + * e.g. becomes sparse. So, we need to recheck that the + * array didn't change size and that there's still a + * valid backing array part. + * + * XXX: alternatively, could prevent finalizers for the + * duration. + */ + if (DUK_UNLIKELY(len != h_arr->length || + h_arr->length > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr))) { + goto skip_fast; + } + + /* Main fast path: arguments array is almost always + * an actual array (though it might also be an arguments + * object). + */ + + DUK_DDD(DUK_DDDPRINT("fast path for %ld elements", (long) h_arr->length)); + tv_src = DUK_HOBJECT_A_GET_BASE(thr->heap, h); + tv_dst = thr->valstack_top; + while (len-- > 0) { + DUK_ASSERT(tv_dst < thr->valstack_end); + if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_src))) { + /* Gaps are very unlikely. Skip over them, + * without an ancestor lookup (technically + * not compliant). + */ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_dst)); /* valstack policy */ + } else { + DUK_TVAL_SET_TVAL(tv_dst, tv_src); + DUK_TVAL_INCREF(thr, tv_dst); + } + tv_src++; + tv_dst++; + } + DUK_ASSERT(tv_dst <= thr->valstack_end); + thr->valstack_top = tv_dst; + return (duk_idx_t) h_arr->length; + } + skip_fast: +#endif /* DUK_USE_ARRAY_FASTPATH */ + + /* Slow path: actual lookups. The initial 'length' lookup + * decides the output length, regardless of side effects that + * may resize or change the argArray while we read the + * indices. + */ + idx = duk_normalize_index(thr, idx); + duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH); + len = duk_to_uint32(thr, -1); /* ToUint32() coercion required */ + if (DUK_UNLIKELY(len >= 0x80000000UL)) { + goto fail_over_2g; + } + duk_pop_unsafe(thr); + DUK_DDD(DUK_DDDPRINT("slow path for %ld elements", (long) len)); + + duk_require_stack(thr, (duk_idx_t) len); + for (i = 0; i < len; i++) { + duk_get_prop_index(thr, idx, (duk_uarridx_t) i); + } + return (duk_idx_t) len; + } else if (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv)) { + return 0; + } + + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return 0;); + + fail_over_2g: + DUK_ERROR_RANGE_INVALID_LENGTH(thr); + DUK_WO_NORETURN(return 0;); } /* * Error throwing */ -DUK_EXTERNAL void duk_throw(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +#if defined(DUK_USE_GCC_PRAGMAS) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" +#elif defined(DUK_USE_CLANG_PRAGMAS) +#pragma clang diagnostic push +#endif +DUK_EXTERNAL void duk_throw_raw(duk_hthread *thr) { + duk_tval *tv_val; + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->valstack_bottom >= thr->valstack); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - if (thr->valstack_top == thr->valstack_bottom) { - DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS); + if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return;); } /* Errors are augmented when they are created, not when they are @@ -19263,77 +24317,134 @@ DUK_EXTERNAL void duk_throw(duk_context *ctx) { duk_hthread_sync_and_null_currpc(thr); #if defined(DUK_USE_AUGMENT_ERROR_THROW) - DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(thr, -1))); duk_err_augment_error_throw(thr); #endif - DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1))); - duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); + tv_val = DUK_GET_TVAL_NEGIDX(thr, -1); + duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, tv_val); +#if defined(DUK_USE_DEBUGGER_SUPPORT) + duk_err_check_debugger_integration(thr); +#endif /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't - * need to check that here. If the value is NULL, a panic occurs because - * we can't return. + * need to check that here. If the value is NULL, a fatal error occurs + * because we can't return. */ duk_err_longjmp(thr); DUK_UNREACHABLE(); } -DUK_EXTERNAL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg) { - duk_hthread *thr = (duk_hthread *) ctx; - - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_fatal_raw(duk_hthread *thr, const char *err_msg) { + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(thr->heap->fatal_func != NULL); - DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s", - (long) err_code, (const char *) err_msg)); + DUK_D(DUK_DPRINT("fatal error occurred: %s", err_msg ? err_msg : "NULL")); /* fatal_func should be noreturn, but noreturn declarations on function * pointers has a very spotty support apparently so it's not currently * done. */ - thr->heap->fatal_func(ctx, err_code, err_msg); + thr->heap->fatal_func(thr->heap->heap_udata, err_msg); - DUK_PANIC(DUK_ERR_API_ERROR, "fatal handler returned"); + /* If the fatal handler returns, all bets are off. It'd be nice to + * print something here but since we don't want to depend on stdio, + * there's no way to do so portably. + */ + DUK_D(DUK_DPRINT("fatal error handler returned, all bets are off!")); + for (;;) { + /* loop forever, don't return (function marked noreturn) */ + } } -DUK_EXTERNAL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_error_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) { + DUK_ASSERT_API_ENTRY(thr); - duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap); - duk_throw(ctx); + duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); + (void) duk_throw(thr); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) { +DUK_EXTERNAL void duk_error_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) { va_list ap; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); va_start(ap, fmt); - duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap); + duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); va_end(ap); - duk_throw(ctx); + (void) duk_throw(thr); + DUK_WO_NORETURN(return;); } +#if defined(DUK_USE_GCC_PRAGMAS) +#pragma GCC diagnostic pop +#elif defined(DUK_USE_CLANG_PRAGMAS) +#pragma clang diagnostic pop +#endif + #if !defined(DUK_USE_VARIADIC_MACROS) -DUK_EXTERNAL void duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) { +DUK_NORETURN(DUK_LOCAL_DECL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap)); + +DUK_LOCAL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap) { const char *filename; duk_int_t line; - va_list ap; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); filename = duk_api_global_filename; line = duk_api_global_line; duk_api_global_filename = NULL; duk_api_global_line = 0; - va_start(ap, fmt); - duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap); - va_end(ap); - duk_throw(ctx); + duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap); + (void) duk_throw(thr); + DUK_WO_NORETURN(return;); +} + +#define DUK__ERROR_STASH_SHARED(code) do { \ + va_list ap; \ + va_start(ap, fmt); \ + duk__throw_error_from_stash(thr, (code), fmt, ap); \ + va_end(ap); \ + DUK_WO_NORETURN(return 0;); \ + } while (0) + +DUK_EXTERNAL duk_ret_t duk_error_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(err_code); +} +DUK_EXTERNAL duk_ret_t duk_generic_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_eval_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_EVAL_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_range_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_RANGE_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_reference_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_REFERENCE_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_syntax_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_SYNTAX_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_type_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_TYPE_ERROR); +} +DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_hthread *thr, const char *fmt, ...) { + DUK_ASSERT_API_ENTRY(thr); + DUK__ERROR_STASH_SHARED(DUK_ERR_URI_ERROR); } #endif /* DUK_USE_VARIADIC_MACROS */ @@ -19341,14 +24452,13 @@ DUK_EXTERNAL void duk_error_stash(duk_context *ctx, duk_errcode_t err_code, cons * Comparison */ -DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_bool_t duk_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { duk_tval *tv1, *tv2; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_get_tval(ctx, index1); - tv2 = duk_get_tval(ctx, index2); + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); if ((tv1 == NULL) || (tv2 == NULL)) { return 0; } @@ -19359,13 +24469,13 @@ DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t return duk_js_equals(thr, tv1, tv2); } -DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) { +DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { duk_tval *tv1, *tv2; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_get_tval(ctx, index1); - tv2 = duk_get_tval(ctx, index2); + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); if ((tv1 == NULL) || (tv2 == NULL)) { return 0; } @@ -19374,14 +24484,29 @@ DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t index1, du return duk_js_strict_equals(tv1, tv2); } +DUK_EXTERNAL duk_bool_t duk_samevalue(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { + duk_tval *tv1, *tv2; + + DUK_ASSERT_API_ENTRY(thr); + + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); + if ((tv1 == NULL) || (tv2 == NULL)) { + return 0; + } + + /* No coercions or other side effects, so safe */ + return duk_js_samevalue(tv1, tv2); +} + /* * instanceof */ -DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) { +DUK_EXTERNAL duk_bool_t duk_instanceof(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) { duk_tval *tv1, *tv2; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* Index validation is strict, which differs from duk_equals(). * The strict behavior mimics how instanceof itself works, e.g. @@ -19389,23 +24514,19 @@ DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t index1, duk_i * be somewhat inconsistent if rval would be allowed to be * non-existent without a TypeError. */ - tv1 = duk_require_tval(ctx, index1); + tv1 = duk_require_tval(thr, idx1); DUK_ASSERT(tv1 != NULL); - tv2 = duk_require_tval(ctx, index2); + tv2 = duk_require_tval(thr, idx2); DUK_ASSERT(tv2 != NULL); - return duk_js_instanceof((duk_hthread *) ctx, tv1, tv2); + return duk_js_instanceof(thr, tv1, tv2); } /* * Lightfunc */ -DUK_INTERNAL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) { - duk_c_function func; - - DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv)); - +DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) { /* Lightfunc name, includes Duktape/C native function pointer, which * can often be used to locate the function from a symbol table. * The name also includes the 16-bit duk_tval flags field because it @@ -19418,20 +24539,37 @@ DUK_INTERNAL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) { * is accessed). */ - func = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv); - duk_push_sprintf(ctx, "light_"); - duk_push_string_funcptr(ctx, (duk_uint8_t *) &func, sizeof(func)); - duk_push_sprintf(ctx, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)); - duk_concat(ctx, 3); + DUK_ASSERT_API_ENTRY(thr); + + duk_push_literal(thr, "light_"); + duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func)); + duk_push_sprintf(thr, "_%04x", (unsigned int) lf_flags); + duk_concat(thr, 3); } -DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) { +DUK_INTERNAL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv) { + duk_c_function func; + duk_small_uint_t lf_flags; + + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv)); - duk_push_string(ctx, "function "); - duk_push_lightfunc_name(ctx, tv); - duk_push_string(ctx, "() {\"light\"}"); - duk_concat(ctx, 3); + DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); + duk_push_lightfunc_name_raw(thr, func, lf_flags); +} + +DUK_INTERNAL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv) { + duk_c_function func; + duk_small_uint_t lf_flags; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv)); + + DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); /* read before 'tv' potentially invalidated */ + duk_push_literal(thr, "function "); + duk_push_lightfunc_name_raw(thr, func, lf_flags); + duk_push_literal(thr, "() { [lightfunc code] }"); + duk_concat(thr, 3); } /* @@ -19441,12 +24579,13 @@ DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) { * bytes from memory. */ -DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz) { +DUK_INTERNAL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz) { duk_uint8_t buf[32 * 2]; duk_uint8_t *p, *q; duk_small_uint_t i; duk_small_uint_t t; + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(sz <= 32); /* sanity limit for function pointer size */ p = buf; @@ -19465,10 +24604,9 @@ DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, du *p++ = duk_lc_digits[t & 0x0f]; } - duk_push_lstring(ctx, (const char *) buf, sz * 2); + duk_push_lstring(thr, (const char *) buf, sz * 2); } -#if !defined(DUK_USE_PARANOID_ERRORS) /* * Push readable string summarizing duk_tval. The operation is side effect * free and will only throw from internal errors (e.g. out of memory). @@ -19476,25 +24614,27 @@ DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, du * and is not intended to be fast (but small and safe). */ -#define DUK__READABLE_STRING_MAXCHARS 32 +/* String limits for summary strings. */ +#define DUK__READABLE_SUMMARY_MAXCHARS 96 /* maximum supported by helper */ +#define DUK__READABLE_STRING_MAXCHARS 32 /* for strings/symbols */ +#define DUK__READABLE_ERRMSG_MAXCHARS 96 /* for error messages */ /* String sanitizer which escapes ASCII control characters and a few other * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with * question marks. No errors are thrown for any input string, except in out * of memory situations. */ -DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring *h_input) { - duk_hthread *thr; +DUK_LOCAL void duk__push_hstring_readable_unicode(duk_hthread *thr, duk_hstring *h_input, duk_small_uint_t maxchars) { const duk_uint8_t *p, *p_start, *p_end; - duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_STRING_MAXCHARS + + duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_SUMMARY_MAXCHARS + 2 /*quotes*/ + 3 /*periods*/]; duk_uint8_t *q; duk_ucodepoint_t cp; duk_small_uint_t nchars; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); DUK_ASSERT(h_input != NULL); - thr = (duk_hthread *) ctx; + DUK_ASSERT(maxchars <= DUK__READABLE_SUMMARY_MAXCHARS); p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); @@ -19507,7 +24647,7 @@ DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring if (p >= p_end) { break; } - if (nchars == DUK__READABLE_STRING_MAXCHARS) { + if (nchars == maxchars) { *q++ = (duk_uint8_t) DUK_ASC_PERIOD; *q++ = (duk_uint8_t) DUK_ASC_PERIOD; *q++ = (duk_uint8_t) DUK_ASC_PERIOD; @@ -19532,74 +24672,189 @@ DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring } *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE; - duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (q - buf)); + duk_push_lstring(thr, (const char *) buf, (duk_size_t) (q - buf)); } -DUK_INTERNAL const char *duk_push_string_tval_readable(duk_context *ctx, duk_tval *tv) { - duk_hthread *thr; - - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); +DUK_LOCAL const char *duk__push_string_tval_readable(duk_hthread *thr, duk_tval *tv, duk_bool_t error_aware) { + DUK_CTX_ASSERT_VALID(thr); + /* 'tv' may be NULL */ if (tv == NULL) { - duk_push_string(ctx, "none"); + duk_push_literal(thr, "none"); } else { switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_STRING: { - duk__push_hstring_readable_unicode(ctx, DUK_TVAL_GET_STRING(tv)); + duk_hstring *h = DUK_TVAL_GET_STRING(tv); + if (DUK_HSTRING_HAS_SYMBOL(h)) { + /* XXX: string summary produces question marks + * so this is not very ideal. + */ + duk_push_literal(thr, "[Symbol "); + duk_push_string(thr, duk__get_symbol_type_string(h)); + duk_push_literal(thr, " "); + duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS); + duk_push_literal(thr, "]"); + duk_concat(thr, 5); + break; + } + duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS); break; } case DUK_TAG_OBJECT: { duk_hobject *h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - duk_push_hobject_class_string(ctx, h); + + if (error_aware && + duk_hobject_prototype_chain_contains(thr, h, thr->builtins[DUK_BIDX_ERROR_PROTOTYPE], 1 /*ignore_loop*/)) { + /* Get error message in a side effect free way if + * possible; if not, summarize as a generic object. + * Error message currently gets quoted. + */ + /* XXX: better internal getprop call; get without side effects + * but traverse inheritance chain. + */ + duk_tval *tv_msg; + tv_msg = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, h, DUK_STRIDX_MESSAGE); + if (tv_msg != NULL && DUK_TVAL_IS_STRING(tv_msg)) { + /* It's critical to avoid recursion so + * only summarize a string .message. + */ + duk__push_hstring_readable_unicode(thr, DUK_TVAL_GET_STRING(tv_msg), DUK__READABLE_ERRMSG_MAXCHARS); + break; + } + } + duk_push_class_string_tval(thr, tv, 1 /*avoid_side_effects*/); break; } case DUK_TAG_BUFFER: { + /* While plain buffers mimic Uint8Arrays, they summarize differently. + * This is useful so that the summarized string accurately reflects the + * internal type which may matter for figuring out bugs etc. + */ /* XXX: Hex encoded, length limited buffer summary here? */ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); DUK_ASSERT(h != NULL); - duk_push_sprintf(ctx, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h)); + duk_push_sprintf(thr, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h)); break; } case DUK_TAG_POINTER: { /* Surround with parentheses like in JX, ensures NULL pointer * is distinguishable from null value ("(null)" vs "null"). */ - duk_push_tval(ctx, tv); - duk_push_sprintf(ctx, "(%s)", duk_to_string(ctx, -1)); - duk_remove(ctx, -2); + duk_push_tval(thr, tv); + duk_push_sprintf(thr, "(%s)", duk_to_string(thr, -1)); + duk_remove_m2(thr); break; } default: { - duk_push_tval(ctx, tv); + duk_push_tval(thr, tv); break; } } } - return duk_to_string(ctx, -1); + return duk_to_string(thr, -1); +} +DUK_INTERNAL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv) { + DUK_ASSERT_API_ENTRY(thr); + return duk__push_string_tval_readable(thr, tv, 0 /*error_aware*/); } -DUK_INTERNAL const char *duk_push_string_readable(duk_context *ctx, duk_idx_t index) { - DUK_ASSERT_CTX_VALID(ctx); - return duk_push_string_tval_readable(ctx, duk_get_tval(ctx, index)); +DUK_INTERNAL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); + return duk_push_string_tval_readable(thr, duk_get_tval(thr, idx)); } -#endif /* !DUK_USE_PARANOID_ERRORS */ +DUK_INTERNAL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv) { + DUK_ASSERT_API_ENTRY(thr); + return duk__push_string_tval_readable(thr, tv, 1 /*error_aware*/); +} + +DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h) { + const duk_uint8_t *p; + const duk_uint8_t *p_end; + const duk_uint8_t *q; + + DUK_ASSERT_API_ENTRY(thr); + + /* .toString() */ + duk_push_literal(thr, "Symbol("); + p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); + p_end = p + DUK_HSTRING_GET_BYTELEN(h); + DUK_ASSERT(p[0] == 0xff || (p[0] & 0xc0) == 0x80); + p++; + for (q = p; q < p_end; q++) { + if (*q == 0xffU) { + /* Terminate either at end-of-string (but NUL MUST + * be accepted without terminating description) or + * 0xFF, which is used to mark start of unique trailer + * (and cannot occur in CESU-8 / extended UTF-8). + */ + break; + } + } + duk_push_lstring(thr, (const char *) p, (duk_size_t) (q - p)); + duk_push_literal(thr, ")"); + duk_concat(thr, 3); +} + +/* + * Functions + */ + +#if 0 /* not used yet */ +DUK_INTERNAL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h) { + duk_c_function func; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)); + + duk_push_sprintf(thr, "native_"); + func = h->func; + duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func)); + duk_push_sprintf(thr, "_%04x_%04x", + (unsigned int) (duk_uint16_t) h->nargs, + (unsigned int) (duk_uint16_t) h->magic); + duk_concat(thr, 3); +} +#endif + +/* + * duk_tval slice copy + */ + +DUK_INTERNAL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count) { + duk_tval *tv; + + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); + DUK_ASSERT(count * sizeof(duk_tval) >= count); /* no wrap */ + + duk_memcpy_unsafe((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval)); + + tv = tv_dst; + while (count-- > 0) { + DUK_TVAL_INCREF(thr, tv); + tv++; + } +} + +/* automatic undefs */ +#undef DUK__ASSERT_SPACE #undef DUK__CHECK_SPACE +#undef DUK__ERROR_STASH_SHARED #undef DUK__PACK_ARGS +#undef DUK__READABLE_ERRMSG_MAXCHARS #undef DUK__READABLE_STRING_MAXCHARS -#line 1 "duk_api_string.c" +#undef DUK__READABLE_SUMMARY_MAXCHARS /* * String manipulation */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, duk_bool_t is_join) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL void duk__concat_and_join_helper(duk_hthread *thr, duk_idx_t count_in, duk_bool_t is_join) { duk_uint_t count; duk_uint_t i; duk_size_t idx; @@ -19607,22 +24862,22 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, duk_hstring *h; duk_uint8_t *buf; - DUK_ASSERT_CTX_VALID(ctx); + DUK_CTX_ASSERT_VALID(thr); if (DUK_UNLIKELY(count_in <= 0)) { if (count_in < 0) { - DUK_ERROR_API(thr, DUK_STR_INVALID_COUNT); - return; + DUK_ERROR_RANGE_INVALID_COUNT(thr); + DUK_WO_NORETURN(return;); } DUK_ASSERT(count_in == 0); - duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); + duk_push_hstring_empty(thr); return; } count = (duk_uint_t) count_in; if (is_join) { duk_size_t t1, t2, limit; - h = duk_to_hstring(ctx, -((duk_idx_t) count) - 1); + h = duk_to_hstring(thr, -((duk_idx_t) count) - 1); DUK_ASSERT(h != NULL); /* A bit tricky overflow test, see doc/code-issues.rst. */ @@ -19630,7 +24885,7 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, t2 = (duk_size_t) (count - 1); limit = (duk_size_t) DUK_HSTRING_MAX_BYTELEN; if (DUK_UNLIKELY(t2 != 0 && t1 > limit / t2)) { - /* Combined size of separators already overflows */ + /* Combined size of separators already overflows. */ goto error_overflow; } len = (duk_size_t) (t1 * t2); @@ -19640,8 +24895,7 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, for (i = count; i >= 1; i--) { duk_size_t new_len; - duk_to_string(ctx, -((duk_idx_t) i)); - h = duk_require_hstring(ctx, -((duk_idx_t) i)); + h = duk_to_hstring(thr, -((duk_idx_t) i)); new_len = len + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h); /* Impose a string maximum length, need to handle overflow @@ -19657,74 +24911,121 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, DUK_DDD(DUK_DDDPRINT("join/concat %lu strings, total length %lu bytes", (unsigned long) count, (unsigned long) len)); - /* use stack allocated buffer to ensure reachability in errors (e.g. intern error) */ - buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, len); + /* Use stack allocated buffer to ensure reachability in errors + * (e.g. intern error). + */ + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len); DUK_ASSERT(buf != NULL); - /* [... (sep) str1 str2 ... strN buf] */ + /* [ ... (sep) str1 str2 ... strN buf ] */ idx = 0; for (i = count; i >= 1; i--) { if (is_join && i != count) { - h = duk_require_hstring(ctx, -((duk_idx_t) count) - 2); /* extra -1 for buffer */ - DUK_MEMCPY(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); + h = duk_require_hstring(thr, -((duk_idx_t) count) - 2); /* extra -1 for buffer */ + duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); idx += DUK_HSTRING_GET_BYTELEN(h); } - h = duk_require_hstring(ctx, -((duk_idx_t) i) - 1); /* extra -1 for buffer */ - DUK_MEMCPY(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); + h = duk_require_hstring(thr, -((duk_idx_t) i) - 1); /* extra -1 for buffer */ + duk_memcpy(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); idx += DUK_HSTRING_GET_BYTELEN(h); } DUK_ASSERT(idx == len); - /* [... (sep) str1 str2 ... strN buf] */ + /* [ ... (sep) str1 str2 ... strN buf ] */ - /* get rid of the strings early to minimize memory use before intern */ + /* Get rid of the strings early to minimize memory use before intern. */ if (is_join) { - duk_replace(ctx, -((duk_idx_t) count) - 2); /* overwrite sep */ - duk_pop_n(ctx, count); + duk_replace(thr, -((duk_idx_t) count) - 2); /* overwrite sep */ + duk_pop_n(thr, (duk_idx_t) count); } else { - duk_replace(ctx, -((duk_idx_t) count) - 1); /* overwrite str1 */ - duk_pop_n(ctx, count-1); + duk_replace(thr, -((duk_idx_t) count) - 1); /* overwrite str1 */ + duk_pop_n(thr, (duk_idx_t) (count - 1)); } - /* [... buf] */ + /* [ ... buf ] */ - (void) duk_to_string(ctx, -1); + (void) duk_buffer_to_string(thr, -1); /* Safe if inputs are safe. */ - /* [... res] */ + /* [ ... res ] */ return; error_overflow: - DUK_ERROR_RANGE(thr, DUK_STR_CONCAT_RESULT_TOO_LONG); + DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); + DUK_WO_NORETURN(return;); } -DUK_EXTERNAL void duk_concat(duk_context *ctx, duk_idx_t count) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_concat(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); - duk__concat_and_join_helper(ctx, count, 0 /*is_join*/); + duk__concat_and_join_helper(thr, count, 0 /*is_join*/); } -DUK_EXTERNAL void duk_join(duk_context *ctx, duk_idx_t count) { - DUK_ASSERT_CTX_VALID(ctx); +#if defined(DUK_USE_PREFER_SIZE) +DUK_INTERNAL void duk_concat_2(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_concat(thr, 2); +} +#else /* DUK_USE_PREFER_SIZE */ +DUK_INTERNAL void duk_concat_2(duk_hthread *thr) { + duk_hstring *h1; + duk_hstring *h2; + duk_uint8_t *buf; + duk_size_t len1; + duk_size_t len2; + duk_size_t len; - duk__concat_and_join_helper(ctx, count, 1 /*is_join*/); + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(duk_get_top(thr) >= 2); /* Trusted caller. */ + + h1 = duk_to_hstring(thr, -2); + h2 = duk_to_hstring(thr, -1); + len1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1); + len2 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2); + len = len1 + len2; + if (DUK_UNLIKELY(len < len1 || /* wrapped */ + len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN)) { + goto error_overflow; + } + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len); + DUK_ASSERT(buf != NULL); + + duk_memcpy((void *) buf, (const void *) DUK_HSTRING_GET_DATA(h1), (size_t) len1); + duk_memcpy((void *) (buf + len1), (const void *) DUK_HSTRING_GET_DATA(h2), (size_t) len2); + (void) duk_buffer_to_string(thr, -1); /* Safe if inputs are safe. */ + + /* [ ... str1 str2 buf ] */ + + duk_replace(thr, -3); + duk_pop_unsafe(thr); + return; + + error_overflow: + DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); + DUK_WO_NORETURN(return;); +} +#endif /* DUK_USE_PREFER_SIZE */ + +DUK_EXTERNAL void duk_join(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); + + duk__concat_and_join_helper(thr, count, 1 /*is_join*/); } /* XXX: could map/decode be unified with duk_unicode_support.c code? * Case conversion needs also the character surroundings though. */ -DUK_EXTERNAL void duk_decode_string(duk_context *ctx, duk_idx_t index, duk_decode_char_function callback, void *udata) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_decode_string(duk_hthread *thr, duk_idx_t idx, duk_decode_char_function callback, void *udata) { duk_hstring *h_input; const duk_uint8_t *p, *p_start, *p_end; duk_codepoint_t cp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - h_input = duk_require_hstring(ctx, index); + h_input = duk_require_hstring(thr, idx); /* Accept symbols. */ DUK_ASSERT(h_input != NULL); p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); @@ -19735,28 +25036,27 @@ DUK_EXTERNAL void duk_decode_string(duk_context *ctx, duk_idx_t index, duk_decod if (p >= p_end) { break; } - cp = (int) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); + cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); callback(udata, cp); } } -DUK_EXTERNAL void duk_map_string(duk_context *ctx, duk_idx_t index, duk_map_char_function callback, void *udata) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_map_string(duk_hthread *thr, duk_idx_t idx, duk_map_char_function callback, void *udata) { duk_hstring *h_input; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; const duk_uint8_t *p, *p_start, *p_end; duk_codepoint_t cp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_normalize_index(ctx, index); + idx = duk_normalize_index(thr, idx); - h_input = duk_require_hstring(ctx, index); + h_input = duk_require_hstring(thr, idx); /* Accept symbols. */ DUK_ASSERT(h_input != NULL); bw = &bw_alloc; - DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* reasonable output estimate */ + DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input)); /* Reasonable output estimate. */ p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input); p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input); @@ -19770,32 +25070,33 @@ DUK_EXTERNAL void duk_map_string(duk_context *ctx, duk_idx_t index, duk_map_char if (p >= p_end) { break; } - cp = (int) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); + cp = (duk_codepoint_t) duk_unicode_decode_xutf8_checked(thr, &p, p_start, p_end); cp = callback(udata, cp); DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp); } DUK_BW_COMPACT(thr, bw); - duk_to_string(ctx, -1); - duk_replace(ctx, index); + (void) duk_buffer_to_string(thr, -1); /* Safe, extended UTF-8 encoded. */ + duk_replace(thr, idx); } -DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t index, duk_size_t start_offset, duk_size_t end_offset) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_substring(duk_hthread *thr, duk_idx_t idx, duk_size_t start_offset, duk_size_t end_offset) { duk_hstring *h; duk_hstring *res; duk_size_t start_byte_offset; duk_size_t end_byte_offset; + duk_size_t charlen; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - h = duk_require_hstring(ctx, index); + idx = duk_require_normalize_index(thr, idx); /* Accept symbols. */ + h = duk_require_hstring(thr, idx); DUK_ASSERT(h != NULL); - if (end_offset >= DUK_HSTRING_GET_CHARLEN(h)) { - end_offset = DUK_HSTRING_GET_CHARLEN(h); + charlen = DUK_HSTRING_GET_CHARLEN(h); + if (end_offset >= charlen) { + end_offset = charlen; } if (start_offset > end_offset) { start_offset = end_offset; @@ -19806,7 +25107,7 @@ DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t index, duk_size_t st DUK_ASSERT_DISABLE(end_offset >= 0); DUK_ASSERT(end_offset >= start_offset && end_offset <= DUK_HSTRING_GET_CHARLEN(h)); - /* guaranteed by string limits */ + /* Guaranteed by string limits. */ DUK_ASSERT(start_offset <= DUK_UINT32_MAX); DUK_ASSERT(end_offset <= DUK_UINT32_MAX); @@ -19814,31 +25115,30 @@ DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t index, duk_size_t st end_byte_offset = (duk_size_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) end_offset); DUK_ASSERT(end_byte_offset >= start_byte_offset); - DUK_ASSERT(end_byte_offset - start_byte_offset <= DUK_UINT32_MAX); /* guaranteed by string limits */ + DUK_ASSERT(end_byte_offset - start_byte_offset <= DUK_UINT32_MAX); /* Guaranteed by string limits. */ - /* no size check is necessary */ - res = duk_heap_string_intern_checked(thr, - DUK_HSTRING_GET_DATA(h) + start_byte_offset, - (duk_uint32_t) (end_byte_offset - start_byte_offset)); + /* No size check is necessary. */ + res = duk_heap_strtable_intern_checked(thr, + DUK_HSTRING_GET_DATA(h) + start_byte_offset, + (duk_uint32_t) (end_byte_offset - start_byte_offset)); - duk_push_hstring(ctx, res); - duk_replace(ctx, index); + duk_push_hstring(thr, res); + duk_replace(thr, idx); } /* XXX: this is quite clunky. Add Unicode helpers to scan backwards and * forwards with a callback to process codepoints? */ -DUK_EXTERNAL void duk_trim(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_trim(duk_hthread *thr, duk_idx_t idx) { duk_hstring *h; const duk_uint8_t *p, *p_start, *p_end, *p_tmp1, *p_tmp2; /* pointers for scanning */ const duk_uint8_t *q_start, *q_end; /* start (incl) and end (excl) of trimmed part */ duk_codepoint_t cp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - index = duk_require_normalize_index(ctx, index); - h = duk_require_hstring(ctx, index); + idx = duk_require_normalize_index(thr, idx); /* Accept symbols. */ + h = duk_require_hstring(thr, idx); DUK_ASSERT(h != NULL); p_start = DUK_HSTRING_GET_DATA(h); @@ -19855,7 +25155,7 @@ DUK_EXTERNAL void duk_trim(duk_context *ctx, duk_idx_t index) { } q_start = p; if (p == p_end) { - /* entire string is whitespace */ + /* Entire string is whitespace. */ q_end = p; goto scan_done; } @@ -19900,124 +25200,152 @@ DUK_EXTERNAL void duk_trim(duk_context *ctx, duk_idx_t index) { return; } - duk_push_lstring(ctx, (const char *) q_start, (duk_size_t) (q_end - q_start)); - duk_replace(ctx, index); + duk_push_lstring(thr, (const char *) q_start, (duk_size_t) (q_end - q_start)); + duk_replace(thr, idx); } -DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_context *ctx, duk_idx_t index, duk_size_t char_offset) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_hthread *thr, duk_idx_t idx, duk_size_t char_offset) { duk_hstring *h; duk_ucodepoint_t cp; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - h = duk_require_hstring(ctx, index); + /* XXX: Share code with String.prototype.charCodeAt? Main difference + * is handling of clamped offsets. + */ + + h = duk_require_hstring(thr, idx); /* Accept symbols. */ DUK_ASSERT(h != NULL); - DUK_ASSERT_DISABLE(char_offset >= 0); /* always true, arg is unsigned */ + DUK_ASSERT_DISABLE(char_offset >= 0); /* Always true, arg is unsigned. */ if (char_offset >= DUK_HSTRING_GET_CHARLEN(h)) { return 0; } - DUK_ASSERT(char_offset <= DUK_UINT_MAX); /* guaranteed by string limits */ - cp = duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) char_offset); + DUK_ASSERT(char_offset <= DUK_UINT_MAX); /* Guaranteed by string limits. */ + cp = duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) char_offset, 0 /*surrogate_aware*/); return (duk_codepoint_t) cp; } -#line 1 "duk_api_var.c" /* - * Variable access + * Date/time. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_EXTERNAL void duk_get_var(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_activation *act; - duk_hstring *h_varname; - duk_small_int_t throw_flag = 1; /* always throw ReferenceError for unresolvable */ - - DUK_ASSERT_CTX_VALID(ctx); - - h_varname = duk_require_hstring(ctx, -1); /* XXX: tostring? */ - DUK_ASSERT(h_varname != NULL); - - act = duk_hthread_get_current_activation(thr); - if (act) { - (void) duk_js_getvar_activation(thr, act, h_varname, throw_flag); /* -> [ ... varname val this ] */ - } else { - /* Outside any activation -> look up from global. */ - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL); - (void) duk_js_getvar_envrec(thr, thr->builtins[DUK_BIDX_GLOBAL_ENV], h_varname, throw_flag); - } - - /* [ ... varname val this ] (because throw_flag == 1, always resolved) */ - - duk_pop(ctx); - duk_remove(ctx, -2); - - /* [ ... val ] */ - - /* Return value would be pointless: because throw_flag==1, we always - * throw if the identifier doesn't resolve. +DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr) { + /* ECMAScript time, with millisecond fractions. Exposed via + * duk_get_now() for example. */ - return; + DUK_UNREF(thr); + return (duk_double_t) DUK_USE_DATE_GET_NOW(thr); } -DUK_EXTERNAL void duk_put_var(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_activation *act; - duk_hstring *h_varname; - duk_tval *tv_val; - duk_small_int_t throw_flag; - - DUK_ASSERT_CTX_VALID(ctx); - - h_varname = duk_require_hstring(ctx, -2); /* XXX: tostring? */ - DUK_ASSERT(h_varname != NULL); - - tv_val = duk_require_tval(ctx, -1); - - throw_flag = duk_is_strict_call(ctx); - - act = duk_hthread_get_current_activation(thr); - if (act) { - duk_js_putvar_activation(thr, act, h_varname, tv_val, throw_flag); /* -> [ ... varname val this ] */ - } else { - /* Outside any activation -> put to global. */ - DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL); - duk_js_putvar_envrec(thr, thr->builtins[DUK_BIDX_GLOBAL_ENV], h_varname, tv_val, throw_flag); - } - - /* [ ... varname val ] */ - - duk_pop_2(ctx); - - /* [ ... ] */ - - return; +DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr) { + /* ECMAScript time without millisecond fractions. Exposed via + * the Date built-in which doesn't allow fractions. + */ + DUK_UNREF(thr); + return (duk_double_t) DUK_FLOOR(DUK_USE_DATE_GET_NOW(thr)); } -DUK_EXTERNAL duk_bool_t duk_del_var(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - - DUK_ERROR_UNIMPLEMENTED_DEFMSG((duk_hthread *) ctx); - return 0; +DUK_INTERNAL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr) { + DUK_UNREF(thr); +#if defined(DUK_USE_GET_MONOTONIC_TIME) + return (duk_double_t) DUK_USE_GET_MONOTONIC_TIME(thr); +#else + return (duk_double_t) DUK_USE_DATE_GET_NOW(thr); +#endif } -DUK_EXTERNAL duk_bool_t duk_has_var(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_double_t duk_get_now(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); - DUK_ERROR_UNIMPLEMENTED_DEFMSG((duk_hthread *) ctx); - return 0; + /* This API intentionally allows millisecond fractions. */ + return duk_time_get_ecmascript_time(thr); +} + +#if 0 /* XXX: worth exposing? */ +DUK_EXTERNAL duk_double_t duk_get_monotonic_time(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); + + return duk_time_get_monotonic_time(thr); +} +#endif + +DUK_EXTERNAL void duk_time_to_components(duk_hthread *thr, duk_double_t timeval, duk_time_components *comp) { + duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; + duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; + duk_uint_t flags; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(comp != NULL); /* XXX: or check? */ + DUK_UNREF(thr); + + /* Convert as one-based, but change month to zero-based to match the + * ECMAScript Date built-in behavior 1:1. + */ + flags = DUK_DATE_FLAG_ONEBASED | DUK_DATE_FLAG_NAN_TO_ZERO; + + duk_bi_date_timeval_to_parts(timeval, parts, dparts, flags); + + /* XXX: sub-millisecond accuracy for the API */ + + DUK_ASSERT(dparts[DUK_DATE_IDX_MONTH] >= 1.0 && dparts[DUK_DATE_IDX_MONTH] <= 12.0); + comp->year = dparts[DUK_DATE_IDX_YEAR]; + comp->month = dparts[DUK_DATE_IDX_MONTH] - 1.0; + comp->day = dparts[DUK_DATE_IDX_DAY]; + comp->hours = dparts[DUK_DATE_IDX_HOUR]; + comp->minutes = dparts[DUK_DATE_IDX_MINUTE]; + comp->seconds = dparts[DUK_DATE_IDX_SECOND]; + comp->milliseconds = dparts[DUK_DATE_IDX_MILLISECOND]; + comp->weekday = dparts[DUK_DATE_IDX_WEEKDAY]; +} + +DUK_EXTERNAL duk_double_t duk_components_to_time(duk_hthread *thr, duk_time_components *comp) { + duk_double_t d; + duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; + duk_uint_t flags; + + DUK_ASSERT_API_ENTRY(thr); + DUK_ASSERT(comp != NULL); /* XXX: or check? */ + DUK_UNREF(thr); + + /* Match Date constructor behavior (with UTC time). Month is given + * as zero-based. Day-of-month is given as one-based so normalize + * it to zero-based as the internal conversion helpers expects all + * components to be zero-based. + */ + flags = 0; + + /* XXX: expensive conversion; use array format in API instead, or unify + * time provider and time API to use same struct? + */ + + dparts[DUK_DATE_IDX_YEAR] = comp->year; + dparts[DUK_DATE_IDX_MONTH] = comp->month; + dparts[DUK_DATE_IDX_DAY] = comp->day - 1.0; + dparts[DUK_DATE_IDX_HOUR] = comp->hours; + dparts[DUK_DATE_IDX_MINUTE] = comp->minutes; + dparts[DUK_DATE_IDX_SECOND] = comp->seconds; + dparts[DUK_DATE_IDX_MILLISECOND] = comp->milliseconds; + dparts[DUK_DATE_IDX_WEEKDAY] = 0; /* ignored */ + + d = duk_bi_date_get_timeval_from_dparts(dparts, flags); + + return d; } -#line 1 "duk_bi_array.c" /* * Array built-ins * - * Note that most Array built-ins are intentionally generic and work even - * when the 'this' binding is not an Array instance. To ensure this, - * Array algorithms do not assume "magical" Array behavior for the "length" - * property, for instance. + * Most Array built-ins are intentionally generic in ECMAScript, and are + * intended to work even when the 'this' binding is not an Array instance. + * This ECMAScript feature is also used by much real world code. For this + * reason the implementations here don't assume exotic Array behavior or + * e.g. presence of a .length property. However, some algorithms have a + * fast path for duk_harray backed actual Array instances, enabled when + * footprint is not a concern. * * XXX: the "Throw" flag should be set for (almost?) all [[Put]] and * [[Delete]] operations, but it's currently false throughout. Go through @@ -20030,7 +25358,6 @@ DUK_EXTERNAL duk_bool_t duk_has_var(duk_context *ctx) { * the unsigned 32-bit range (E5.1 Section 15.4.5.1 throws a RangeError if so) * some intermediate values may be above 0xffffffff and this may not be always * correctly handled now (duk_uint32_t is not enough for all algorithms). - * * For instance, push() can legitimately write entries beyond length 0xffffffff * and cause a RangeError only at the end. To do this properly, the current * push() implementation tracks the array index using a 'double' instead of a @@ -20047,86 +25374,141 @@ DUK_EXTERNAL duk_bool_t duk_has_var(duk_context *ctx) { * Both "put" and "define" are used in the E5.1 specification; as a rule, * "put" is used when modifying an existing array (or a non-array 'this' * binding) and "define" for setting values into a fresh result array. - * - * Also note that Array instance 'length' should be writable, but not - * enumerable and definitely not configurable: even Duktape code internally - * assumes that an Array instance will always have a 'length' property. - * Preventing deletion of the property is critical. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* Perform an intermediate join when this many elements have been pushed * on the value stack. */ #define DUK__ARRAY_MID_JOIN_LIMIT 4096 -/* Shared entry code for many Array built-ins. Note that length is left - * on stack (it could be popped, but that's not necessary). +#if defined(DUK_USE_ARRAY_BUILTIN) + +/* + * Shared helpers. */ -DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_context *ctx) { + +/* Shared entry code for many Array built-ins: the 'this' binding is pushed + * on the value stack and object coerced, and the current .length is returned. + * Note that length is left on stack (it could be popped, but that's not + * usually necessary because call handling will clean it up automatically). + */ +DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_hthread *thr) { duk_uint32_t len; - (void) duk_push_this_coercible_to_object(ctx); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH); - len = duk_to_uint32(ctx, -1); + /* XXX: push more directly? */ + (void) duk_push_this_coercible_to_object(thr); + DUK_HOBJECT_ASSERT_VALID(duk_get_hobject(thr, -1)); + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_LENGTH); + len = duk_to_uint32(thr, -1); /* -> [ ... ToObject(this) ToUint32(length) ] */ return len; } -DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) { +DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_hthread *thr) { /* Range limited to [0, 0x7fffffff] range, i.e. range that can be * represented with duk_int32_t. Use this when the method doesn't * handle the full 32-bit unsigned range correctly. */ - duk_uint32_t ret = duk__push_this_obj_len_u32(ctx); + duk_uint32_t ret = duk__push_this_obj_len_u32(thr); if (DUK_UNLIKELY(ret >= 0x80000000UL)) { - DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_ARRAY_LENGTH_OVER_2G); + DUK_ERROR_RANGE_INVALID_LENGTH(thr); + DUK_WO_NORETURN(return 0U;); } return ret; } +#if defined(DUK_USE_ARRAY_FASTPATH) +/* Check if 'this' binding is an Array instance (duk_harray) which satisfies + * a few other guarantees for fast path operation. The fast path doesn't + * need to handle all operations, even for duk_harrays, but must handle a + * significant fraction to improve performance. Return a non-NULL duk_harray + * pointer when all fast path criteria are met, NULL otherwise. + */ +DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_hthread *thr) { + duk_tval *tv; + duk_hobject *h; + duk_uint_t flags_mask, flags_bits, flags_value; + + DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* because call in progress */ + tv = DUK_GET_THIS_TVAL_PTR(thr); + + /* Fast path requires that 'this' is a duk_harray. Read only arrays + * (ROM backed) are also rejected for simplicity. + */ + if (!DUK_TVAL_IS_OBJECT(tv)) { + DUK_DD(DUK_DDPRINT("reject array fast path: not an object")); + return NULL; + } + h = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + flags_mask = DUK_HOBJECT_FLAG_ARRAY_PART | \ + DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \ + DUK_HEAPHDR_FLAG_READONLY; + flags_bits = DUK_HOBJECT_FLAG_ARRAY_PART | \ + DUK_HOBJECT_FLAG_EXOTIC_ARRAY; + flags_value = DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) h); + if ((flags_value & flags_mask) != flags_bits) { + DUK_DD(DUK_DDPRINT("reject array fast path: object flag check failed")); + return NULL; + } + + /* In some cases a duk_harray's 'length' may be larger than the + * current array part allocation. Avoid the fast path in these + * cases, so that all fast path code can safely assume that all + * items in the range [0,length[ are backed by the current array + * part allocation. + */ + if (((duk_harray *) h)->length > DUK_HOBJECT_GET_ASIZE(h)) { + DUK_DD(DUK_DDPRINT("reject array fast path: length > array part size")); + return NULL; + } + + /* Guarantees for fast path. */ + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0 || DUK_HOBJECT_A_GET_BASE(thr->heap, h) != NULL); + DUK_ASSERT(((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h)); + + DUK_DD(DUK_DDPRINT("array fast path allowed for: %!O", (duk_heaphdr *) h)); + return (duk_harray *) h; +} +#endif /* DUK_USE_ARRAY_FASTPATH */ + /* * Constructor */ -DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_hthread *thr) { duk_idx_t nargs; + duk_harray *a; duk_double_t d; duk_uint32_t len; - duk_idx_t i; + duk_uint32_t len_prealloc; - nargs = duk_get_top(ctx); - duk_push_array(ctx); + nargs = duk_get_top(thr); - if (nargs == 1 && duk_is_number(ctx, 0)) { + if (nargs == 1 && duk_is_number(thr, 0)) { /* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */ - d = duk_get_number(ctx, 0); - len = duk_to_uint32(ctx, 0); - if (((duk_double_t) len) != d) { - return DUK_RET_RANGE_ERROR; + d = duk_get_number(thr, 0); + len = duk_to_uint32(thr, 0); + if (!duk_double_equals((duk_double_t) len, d)) { + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); } - /* XXX: if 'len' is low, may want to ensure array part is kept: - * the caller is likely to want a dense array. + /* For small lengths create a dense preallocated array. + * For large arrays preallocate an initial part. */ - duk_push_u32(ctx, len); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* [ ToUint32(len) array ToUint32(len) ] -> [ ToUint32(len) array ] */ + len_prealloc = len < 64 ? len : 64; + a = duk_push_harray_with_size(thr, len_prealloc); + DUK_ASSERT(a != NULL); + DUK_ASSERT(!duk_is_bare_object(thr, -1)); + a->length = len; return 1; } - /* XXX: optimize by creating array into correct size directly, and - * operating on the array part directly; values can be memcpy()'d from - * value stack directly as long as refcounts are increased. - */ - for (i = 0; i < nargs; i++) { - duk_dup(ctx, i); - duk_xdef_prop_index_wec(ctx, -2, (duk_uarridx_t) i); - } - - duk_push_u32(ctx, (duk_uint32_t) nargs); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + duk_pack(thr, nargs); return 1; } @@ -20134,11 +25516,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) { * isArray() */ -DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx) { - duk_hobject *h; - - h = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_ARRAY); - duk_push_boolean(ctx, (h != NULL)); +DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 1); + duk_push_boolean(thr, duk_js_isarray(DUK_GET_TVAL_POSIDX(thr, 0))); return 1; } @@ -20146,12 +25526,12 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx) { * toString() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { - (void) duk_push_this_coercible_to_object(ctx); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_JOIN); +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_hthread *thr) { + (void) duk_push_this_coercible_to_object(thr); + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_JOIN); /* [ ... this func ] */ - if (!duk_is_callable(ctx, -1)) { + if (!duk_is_callable(thr, -1)) { /* Fall back to the initial (original) Object.toString(). We don't * currently have pointers to the built-in functions, only the top * level global objects (like "Array") so this is now done in a bit @@ -20163,20 +25543,20 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { * but should have no visible side effects. */ DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString")); - duk_set_top(ctx, 0); - return duk_bi_object_prototype_to_string(ctx); /* has access to 'this' binding */ + duk_set_top(thr, 0); + return duk_bi_object_prototype_to_string(thr); /* has access to 'this' binding */ } /* [ ... this func ] */ - duk_insert(ctx, -2); + duk_insert(thr, -2); /* [ ... func this ] */ DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT", - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); - duk_call_method(ctx, 0); + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); + duk_call_method(thr, 0); return 1; } @@ -20185,21 +25565,27 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { * concat() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_hthread *thr) { duk_idx_t i, n; - duk_uarridx_t idx, idx_last; - duk_uarridx_t j, len; + duk_uint32_t j, idx, len; duk_hobject *h; + duk_size_t tmp_len; - /* XXX: the insert here is a bit expensive if there are a lot of items. + /* XXX: In ES2015 Array .length can be up to 2^53-1. The current + * implementation is limited to 2^32-1. + */ + + /* XXX: Fast path for array 'this' and array element. */ + + /* XXX: The insert here is a bit expensive if there are a lot of items. * It could also be special cased in the outermost for loop quite easily * (as the element is dup()'d anyway). */ - (void) duk_push_this_coercible_to_object(ctx); - duk_insert(ctx, 0); - n = duk_get_top(ctx); - duk_push_array(ctx); /* -> [ ToObject(this) item1 ... itemN arr ] */ + (void) duk_push_this_coercible_to_object(thr); + duk_insert(thr, 0); + n = duk_get_top(thr); + duk_push_array(thr); /* -> [ ToObject(this) item1 ... itemN arr ] */ /* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index() * (which differs from the official algorithm). If no error is thrown, this @@ -20209,59 +25595,97 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { */ idx = 0; - idx_last = 0; for (i = 0; i < n; i++) { - DUK_ASSERT_TOP(ctx, n + 1); + duk_bool_t spreadable; + duk_bool_t need_has_check; + + DUK_ASSERT_TOP(thr, n + 1); /* [ ToObject(this) item1 ... itemN arr ] */ - duk_dup(ctx, i); - h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY); - if (!h) { - duk_xdef_prop_index_wec(ctx, -2, idx++); - idx_last = idx; + h = duk_get_hobject(thr, i); + + if (h == NULL) { + spreadable = 0; + } else { +#if defined(DUK_USE_SYMBOL_BUILTIN) + duk_get_prop_stridx(thr, i, DUK_STRIDX_WELLKNOWN_SYMBOL_IS_CONCAT_SPREADABLE); + if (duk_is_undefined(thr, -1)) { + spreadable = duk_js_isarray_hobject(h); + } else { + spreadable = duk_to_boolean(thr, -1); + } + duk_pop_nodecref_unsafe(thr); +#else + spreadable = duk_js_isarray_hobject(h); +#endif + } + + if (!spreadable) { + duk_dup(thr, i); + duk_xdef_prop_index_wec(thr, -2, idx); + idx++; + if (DUK_UNLIKELY(idx == 0U)) { + /* Index after update is 0, and index written + * was 0xffffffffUL which is no longer a valid + * array index. + */ + goto fail_wrap; + } continue; } - /* [ ToObject(this) item1 ... itemN arr item(i) ] */ + DUK_ASSERT(duk_is_object(thr, i)); + need_has_check = (DUK_HOBJECT_IS_PROXY(h) != 0); /* Always 0 w/o Proxy support. */ - /* XXX: an array can have length higher than 32 bits; this is not handled - * correctly now. - */ - len = (duk_uarridx_t) duk_get_length(ctx, -1); - for (j = 0; j < len; j++) { - if (duk_get_prop_index(ctx, -1, j)) { - /* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */ - duk_xdef_prop_index_wec(ctx, -3, idx++); - idx_last = idx; - } else { - idx++; - duk_pop(ctx); -#if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER) - /* According to E5.1 Section 15.4.4.4 nonexistent trailing - * elements do not affect 'length' of the result. Test262 - * and other engines disagree, so update idx_last here too. - */ - idx_last = idx; -#else - /* Strict standard behavior, ignore trailing elements for - * result 'length'. - */ -#endif - } + /* [ ToObject(this) item1 ... itemN arr ] */ + + tmp_len = duk_get_length(thr, i); + len = (duk_uint32_t) tmp_len; + if (DUK_UNLIKELY(tmp_len != (duk_size_t) len)) { + goto fail_wrap; + } + if (DUK_UNLIKELY(idx + len < idx)) { + /* Result length must be at most 0xffffffffUL to be + * a valid 32-bit array index. + */ + goto fail_wrap; + } + for (j = 0; j < len; j++) { + /* For a Proxy element, an explicit 'has' check is + * needed to allow the Proxy to present gaps. + */ + if (need_has_check) { + if (duk_has_prop_index(thr, i, j)) { + duk_get_prop_index(thr, i, j); + duk_xdef_prop_index_wec(thr, -2, idx); + } + } else { + if (duk_get_prop_index(thr, i, j)) { + duk_xdef_prop_index_wec(thr, -2, idx); + } else { + duk_pop_undefined(thr); + } + } + idx++; + DUK_ASSERT(idx != 0U); /* Wrap check above. */ } - duk_pop(ctx); } - /* The E5.1 Section 15.4.4.4 algorithm doesn't set the length explicitly - * in the end, but because we're operating with an internal value which - * is known to be an array, this should be equivalent. + /* ES5.1 has a specification "bug" in that nonexistent trailing + * elements don't affect the result .length. Test262 and other + * engines disagree, and the specification bug was fixed in ES2015 + * (see NOTE 1 in https://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat). */ - duk_push_uarridx(ctx, idx_last); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + duk_push_uarridx(thr, idx); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - DUK_ASSERT_TOP(ctx, n + 1); + DUK_ASSERT_TOP(thr, n + 1); return 1; + + fail_wrap: + DUK_ERROR_RANGE_INVALID_LENGTH(thr); + DUK_WO_NORETURN(return 0;); } /* @@ -20277,53 +25701,54 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { * There is no fancy handling; the prefix gets re-joined multiple times. */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_hthread *thr) { duk_uint32_t len, count; duk_uint32_t idx; - duk_small_int_t to_locale_string = duk_get_current_magic(ctx); + duk_small_int_t to_locale_string = duk_get_current_magic(thr); duk_idx_t valstack_required; /* For join(), nargs is 1. For toLocaleString(), nargs is 0 and * setting the top essentially pushes an undefined to the stack, * thus defaulting to a comma separator. */ - duk_set_top(ctx, 1); - if (duk_is_undefined(ctx, 0)) { - duk_pop(ctx); - duk_push_hstring_stridx(ctx, DUK_STRIDX_COMMA); + duk_set_top(thr, 1); + if (duk_is_undefined(thr, 0)) { + duk_pop_undefined(thr); + duk_push_hstring_stridx(thr, DUK_STRIDX_COMMA); } else { - duk_to_string(ctx, 0); + duk_to_string(thr, 0); } - len = duk__push_this_obj_len_u32(ctx); + len = duk__push_this_obj_len_u32(thr); /* [ sep ToObject(this) len ] */ DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu", - (duk_tval *) duk_get_tval(ctx, 0), - (duk_tval *) duk_get_tval(ctx, 1), + (duk_tval *) duk_get_tval(thr, 0), + (duk_tval *) duk_get_tval(thr, 1), (unsigned long) len)); /* The extra (+4) is tight. */ - valstack_required = (len >= DUK__ARRAY_MID_JOIN_LIMIT ? - DUK__ARRAY_MID_JOIN_LIMIT : len) + 4; - duk_require_stack(ctx, valstack_required); + valstack_required = (duk_idx_t) ((len >= DUK__ARRAY_MID_JOIN_LIMIT ? + DUK__ARRAY_MID_JOIN_LIMIT : len) + 4); + duk_require_stack(thr, valstack_required); - duk_dup(ctx, 0); + duk_dup_0(thr); /* [ sep ToObject(this) len sep ] */ count = 0; idx = 0; for (;;) { + DUK_DDD(DUK_DDDPRINT("join idx=%ld", (long) idx)); if (count >= DUK__ARRAY_MID_JOIN_LIMIT || /* intermediate join to avoid valstack overflow */ idx >= len) { /* end of loop (careful with len==0) */ /* [ sep ToObject(this) len sep str0 ... str(count-1) ] */ DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld", (long) count, (long) idx, (long) len)); - duk_join(ctx, (duk_idx_t) count); /* -> [ sep ToObject(this) len str ] */ - duk_dup(ctx, 0); /* -> [ sep ToObject(this) len str sep ] */ - duk_insert(ctx, -2); /* -> [ sep ToObject(this) len sep str ] */ + duk_join(thr, (duk_idx_t) count); /* -> [ sep ToObject(this) len str ] */ + duk_dup_0(thr); /* -> [ sep ToObject(this) len str sep ] */ + duk_insert(thr, -2); /* -> [ sep ToObject(this) len sep str ] */ count = 1; } if (idx >= len) { @@ -20331,20 +25756,18 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { break; } - duk_get_prop_index(ctx, 1, (duk_uarridx_t) idx); - if (duk_is_null_or_undefined(ctx, -1)) { - duk_pop(ctx); - duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); + duk_get_prop_index(thr, 1, (duk_uarridx_t) idx); + if (duk_is_null_or_undefined(thr, -1)) { + duk_pop_nodecref_unsafe(thr); + duk_push_hstring_empty(thr); } else { if (to_locale_string) { - duk_to_object(ctx, -1); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_LOCALE_STRING); - duk_insert(ctx, -2); /* -> [ ... toLocaleString ToObject(val) ] */ - duk_call_method(ctx, 0); - duk_to_string(ctx, -1); - } else { - duk_to_string(ctx, -1); + duk_to_object(thr, -1); + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_LOCALE_STRING); + duk_insert(thr, -2); /* -> [ ... toLocaleString ToObject(val) ] */ + duk_call_method(thr, 0); } + duk_to_string(thr, -1); } count++; @@ -20360,27 +25783,129 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { * pop(), push() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx) { +#if defined(DUK_USE_ARRAY_FASTPATH) +DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_hthread *thr, duk_harray *h_arr) { + duk_tval *tv_arraypart; + duk_tval *tv_val; + duk_uint32_t len; + + tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr); + len = h_arr->length; + if (len <= 0) { + /* nop, return undefined */ + return 0; + } + + len--; + h_arr->length = len; + + /* Fast path doesn't check for an index property inherited from + * Array.prototype. This is quite often acceptable; if not, + * disable fast path. + */ + DUK_ASSERT_VS_SPACE(thr); + tv_val = tv_arraypart + len; + if (DUK_TVAL_IS_UNUSED(tv_val)) { + /* No net refcount change. Value stack already has + * 'undefined' based on value stack init policy. + */ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); + DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv_val)); + } else { + /* No net refcount change. */ + DUK_TVAL_SET_TVAL(thr->valstack_top, tv_val); + DUK_TVAL_SET_UNUSED(tv_val); + } + thr->valstack_top++; + + /* XXX: there's no shrink check in the fast path now */ + + return 1; +} +#endif /* DUK_USE_ARRAY_FASTPATH */ + +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_hthread *thr) { duk_uint32_t len; duk_uint32_t idx; +#if defined(DUK_USE_ARRAY_FASTPATH) + duk_harray *h_arr; +#endif - DUK_ASSERT_TOP(ctx, 0); - len = duk__push_this_obj_len_u32(ctx); + DUK_ASSERT_TOP(thr, 0); + +#if defined(DUK_USE_ARRAY_FASTPATH) + h_arr = duk__arraypart_fastpath_this(thr); + if (h_arr) { + return duk__array_pop_fastpath(thr, h_arr); + } +#endif + + /* XXX: Merge fastpath check into a related call (push this, coerce length, etc)? */ + + len = duk__push_this_obj_len_u32(thr); if (len == 0) { - duk_push_int(ctx, 0); - duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH); + duk_push_int(thr, 0); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); return 0; } idx = len - 1; - duk_get_prop_index(ctx, 0, (duk_uarridx_t) idx); - duk_del_prop_index(ctx, 0, (duk_uarridx_t) idx); - duk_push_u32(ctx, idx); - duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH); + duk_get_prop_index(thr, 0, (duk_uarridx_t) idx); + duk_del_prop_index(thr, 0, (duk_uarridx_t) idx); + duk_push_u32(thr, idx); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { +#if defined(DUK_USE_ARRAY_FASTPATH) +DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_hthread *thr, duk_harray *h_arr) { + duk_tval *tv_arraypart; + duk_tval *tv_src; + duk_tval *tv_dst; + duk_uint32_t len; + duk_idx_t i, n; + + len = h_arr->length; + tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr); + + n = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + DUK_ASSERT(n >= 0); + DUK_ASSERT((duk_uint32_t) n <= DUK_UINT32_MAX); + if (DUK_UNLIKELY(len + (duk_uint32_t) n < len)) { + DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw")); + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); /* != 0 return value returned as is by caller */ + } + if (len + (duk_uint32_t) n > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr)) { + /* Array part would need to be extended. Rely on slow path + * for now. + * + * XXX: Rework hobject code a bit and add extend support. + */ + return 0; + } + + tv_src = thr->valstack_bottom; + tv_dst = tv_arraypart + len; + for (i = 0; i < n; i++) { + /* No net refcount change; reset value stack values to + * undefined to satisfy value stack init policy. + */ + DUK_TVAL_SET_TVAL(tv_dst, tv_src); + DUK_TVAL_SET_UNDEFINED(tv_src); + tv_src++; + tv_dst++; + } + thr->valstack_top = thr->valstack_bottom; + len += (duk_uint32_t) n; + h_arr->length = len; + + DUK_ASSERT((duk_uint_t) len == len); + duk_push_uint(thr, (duk_uint_t) len); + return 1; +} +#endif /* DUK_USE_ARRAY_FASTPATH */ + +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_hthread *thr) { /* Note: 'this' is not necessarily an Array object. The push() * algorithm is supposed to work for other kinds of objects too, * so the algorithm has e.g. an explicit update for the 'length' @@ -20389,9 +25914,24 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { duk_uint32_t len; duk_idx_t i, n; +#if defined(DUK_USE_ARRAY_FASTPATH) + duk_harray *h_arr; +#endif - n = duk_get_top(ctx); - len = duk__push_this_obj_len_u32(ctx); +#if defined(DUK_USE_ARRAY_FASTPATH) + h_arr = duk__arraypart_fastpath_this(thr); + if (h_arr) { + duk_ret_t rc; + rc = duk__array_push_fastpath(thr, h_arr); + if (rc != 0) { + return rc; + } + DUK_DD(DUK_DDPRINT("array push() fast path exited, resize case")); + } +#endif + + n = duk_get_top(thr); + len = duk__push_this_obj_len_u32(thr); /* [ arg1 ... argN obj length ] */ @@ -20407,18 +25947,18 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { if (len + (duk_uint32_t) n < len) { DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw")); - return DUK_RET_RANGE_ERROR; + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); } for (i = 0; i < n; i++) { - duk_dup(ctx, i); - duk_put_prop_index(ctx, -3, len + i); + duk_dup(thr, i); + duk_put_prop_index(thr, -3, (duk_uarridx_t) (len + (duk_uint32_t) i)); } - len += n; + len += (duk_uint32_t) n; - duk_push_u32(ctx, len); - duk_dup_top(ctx); - duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH); + duk_push_u32(thr, len); + duk_dup_top(thr); + duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); /* [ arg1 ... argN obj length new_length ] */ return 1; @@ -20434,7 +25974,7 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { * may use a negative offset. */ -DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t idx1, duk_int_t idx2) { +DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_hthread *thr, duk_int_t idx1, duk_int_t idx2) { duk_bool_t have1, have2; duk_bool_t undef1, undef2; duk_small_int_t ret; @@ -20463,12 +26003,12 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id return 0; } - have1 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx1); - have2 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx2); + have1 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx1); + have2 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx2); DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T", (long) idx1, (long) idx2, (long) have1, (long) have2, - (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1))); if (have1) { if (have2) { @@ -20487,8 +26027,8 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id } } - undef1 = duk_is_undefined(ctx, -2); - undef2 = duk_is_undefined(ctx, -1); + undef1 = duk_is_undefined(thr, -2); + undef2 = duk_is_undefined(thr, -1); if (undef1) { if (undef2) { ret = 0; @@ -20506,40 +26046,41 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id } } - if (!duk_is_undefined(ctx, idx_fn)) { + if (!duk_is_undefined(thr, idx_fn)) { duk_double_t d; - /* no need to check callable; duk_call() will do that */ - duk_dup(ctx, idx_fn); /* -> [ ... x y fn ] */ - duk_insert(ctx, -3); /* -> [ ... fn x y ] */ - duk_call(ctx, 2); /* -> [ ... res ] */ + /* No need to check callable; duk_call() will do that. */ + duk_dup(thr, idx_fn); /* -> [ ... x y fn ] */ + duk_insert(thr, -3); /* -> [ ... fn x y ] */ + duk_call(thr, 2); /* -> [ ... res ] */ - /* The specification is a bit vague what to do if the return - * value is not a number. Other implementations seem to - * tolerate non-numbers but e.g. V8 won't apparently do a - * ToNumber(). + /* ES5 is a bit vague about what to do if the return value is + * not a number. ES2015 provides a concrete description: + * http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare. */ - /* XXX: best behavior for real world compatibility? */ - - d = duk_to_number(ctx, -1); + d = duk_to_number_m1(thr); if (d < 0.0) { ret = -1; } else if (d > 0.0) { ret = 1; } else { + /* Because NaN compares to false, NaN is handled here + * without an explicit check above. + */ ret = 0; } - duk_pop(ctx); + duk_pop_nodecref_unsafe(thr); DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret)); return ret; } /* string compare is the default (a bit oddly) */ - h1 = duk_to_hstring(ctx, -2); - h2 = duk_to_hstring(ctx, -1); + /* XXX: any special handling for plain array; causes repeated coercion now? */ + h1 = duk_to_hstring(thr, -2); + h2 = duk_to_hstring_m1(thr); DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); @@ -20547,12 +26088,12 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id goto pop_ret; pop_ret: - duk_pop_2(ctx); + duk_pop_2_unsafe(thr); DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret)); return ret; } -DUK_LOCAL void duk__array_sort_swap(duk_context *ctx, duk_int_t l, duk_int_t r) { +DUK_LOCAL void duk__array_sort_swap(duk_hthread *thr, duk_int_t l, duk_int_t r) { duk_bool_t have_l, have_r; duk_idx_t idx_obj = 1; /* fixed offset in valstack */ @@ -20561,32 +26102,32 @@ DUK_LOCAL void duk__array_sort_swap(duk_context *ctx, duk_int_t l, duk_int_t r) } /* swap elements; deal with non-existent elements correctly */ - have_l = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) l); - have_r = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) r); + have_l = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) l); + have_r = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) r); if (have_r) { /* right exists, [[Put]] regardless whether or not left exists */ - duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) l); + duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) l); } else { - duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) l); - duk_pop(ctx); + duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) l); + duk_pop_undefined(thr); } if (have_l) { - duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) r); + duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) r); } else { - duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) r); - duk_pop(ctx); + duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) r); + duk_pop_undefined(thr); } } -#if defined(DUK_USE_DDDPRINT) +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) /* Debug print which visualizes the qsort partitioning process. */ -DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int_t hi, duk_int_t pivot) { +DUK_LOCAL void duk__debuglog_qsort_state(duk_hthread *thr, duk_int_t lo, duk_int_t hi, duk_int_t pivot) { char buf[4096]; char *ptr = buf; duk_int_t i, n; - n = (duk_int_t) duk_get_length(ctx, 1); + n = (duk_int_t) duk_get_length(thr, 1); if (n > 4000) { n = 4000; } @@ -20612,16 +26153,15 @@ DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int } #endif -DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL void duk__array_qsort(duk_hthread *thr, duk_int_t lo, duk_int_t hi) { duk_int_t p, l, r; /* The lo/hi indices may be crossed and hi < 0 is possible at entry. */ DUK_DDD(DUK_DDDPRINT("duk__array_qsort: lo=%ld, hi=%ld, obj=%!T", - (long) lo, (long) hi, (duk_tval *) duk_get_tval(ctx, 1))); + (long) lo, (long) hi, (duk_tval *) duk_get_tval(thr, 1))); - DUK_ASSERT_TOP(ctx, 3); + DUK_ASSERT_TOP(thr, 3); /* In some cases it may be that lo > hi, or hi < 0; these * degenerate cases happen e.g. for empty arrays, and in @@ -20637,15 +26177,14 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { DUK_ASSERT(hi - lo + 1 >= 2); /* randomized pivot selection */ - p = lo + (duk_util_tinyrandom_get_bits(thr, 30) % (hi - lo + 1)); /* rnd in [lo,hi] */ + p = lo + (duk_int_t) (DUK_UTIL_GET_RANDOM_DOUBLE(thr) * (duk_double_t) (hi - lo + 1)); DUK_ASSERT(p >= lo && p <= hi); - DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", - (long) lo, (long) hi, (long) p)); + DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", (long) lo, (long) hi, (long) p)); /* move pivot out of the way */ - duk__array_sort_swap(ctx, p, lo); + duk__array_sort_swap(thr, p, lo); p = lo; - DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(ctx, 1))); + DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(thr, 1))); l = lo + 1; r = hi; @@ -20657,7 +26196,7 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { if (l >= hi) { break; } - if (duk__array_sort_compare(ctx, l, p) >= 0) { /* !(l < p) */ + if (duk__array_sort_compare(thr, l, p) >= 0) { /* !(l < p) */ break; } l++; @@ -20668,7 +26207,7 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { if (r <= lo) { break; } - if (duk__array_sort_compare(ctx, p, r) >= 0) { /* !(p < r) */ + if (duk__array_sort_compare(thr, p, r) >= 0) { /* !(p < r) */ break; } r--; @@ -20680,9 +26219,9 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r)); - duk__array_sort_swap(ctx, l, r); + duk__array_sort_swap(thr, l, r); - DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(ctx, 1))); + DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(thr, 1))); l++; r--; } @@ -20698,25 +26237,25 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { */ /* move pivot to its final place */ - DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(ctx, 1))); - duk__array_sort_swap(ctx, lo, r); + DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(thr, 1))); + duk__array_sort_swap(thr, lo, r); -#if defined(DUK_USE_DDDPRINT) - duk__debuglog_qsort_state(ctx, lo, hi, r); +#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) + duk__debuglog_qsort_state(thr, lo, hi, r); #endif - DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(ctx, 1))); - duk__array_qsort(ctx, lo, r - 1); - duk__array_qsort(ctx, r + 1, hi); + DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(thr, 1))); + duk__array_qsort(thr, lo, r - 1); + duk__array_qsort(thr, r + 1, hi); } -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_hthread *thr) { duk_uint32_t len; /* XXX: len >= 0x80000000 won't work below because a signed type * is needed by qsort. */ - len = duk__push_this_obj_len_u32_limited(ctx); + len = duk__push_this_obj_len_u32_limited(thr); /* stack[0] = compareFn * stack[1] = ToObject(this) @@ -20725,11 +26264,11 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) { if (len > 0) { /* avoid degenerate cases, so that (len - 1) won't underflow */ - duk__array_qsort(ctx, (duk_int_t) 0, (duk_int_t) (len - 1)); + duk__array_qsort(thr, (duk_int_t) 0, (duk_int_t) (len - 1)); } - DUK_ASSERT_TOP(ctx, 3); - duk_pop(ctx); + DUK_ASSERT_TOP(thr, 3); + duk_pop_nodecref_unsafe(thr); return 1; /* return ToObject(this) */ } @@ -20746,9 +26285,10 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) { * unshift is (close to?) <--> splice(0, 0, [items])? */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_hthread *thr) { duk_idx_t nargs; - duk_uint32_t len; + duk_uint32_t len_u32; + duk_int_t len; duk_bool_t have_delcount; duk_int_t item_count; duk_int_t act_start; @@ -20757,9 +26297,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { DUK_UNREF(have_delcount); - nargs = duk_get_top(ctx); + nargs = duk_get_top(thr); if (nargs < 2) { - duk_set_top(ctx, 2); + duk_set_top(thr, 2); nargs = 2; have_delcount = 0; } else { @@ -20769,19 +26309,21 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { /* XXX: len >= 0x80000000 won't work below because we need to be * able to represent -len. */ - len = duk__push_this_obj_len_u32_limited(ctx); + len_u32 = duk__push_this_obj_len_u32_limited(thr); + len = (duk_int_t) len_u32; + DUK_ASSERT(len >= 0); - act_start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len); + act_start = duk_to_int_clamped(thr, 0, -len, len); if (act_start < 0) { act_start = len + act_start; } - DUK_ASSERT(act_start >= 0 && act_start <= (duk_int_t) len); + DUK_ASSERT(act_start >= 0 && act_start <= len); -#ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT +#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT) if (have_delcount) { #endif - del_count = duk_to_int_clamped(ctx, 1, 0, len - act_start); -#ifdef DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT + del_count = duk_to_int_clamped(thr, 1, 0, len - act_start); +#if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT) } else { /* E5.1 standard behavior when deleteCount is not given would be * to treat it just like if 'undefined' was given, which coerces @@ -20795,16 +26337,16 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { DUK_ASSERT(nargs >= 2); item_count = (duk_int_t) (nargs - 2); - DUK_ASSERT(del_count >= 0 && del_count <= (duk_int_t) len - act_start); - DUK_ASSERT(del_count + act_start <= (duk_int_t) len); + DUK_ASSERT(del_count >= 0 && del_count <= len - act_start); + DUK_ASSERT(del_count + act_start <= len); /* For now, restrict result array into 32-bit length range. */ if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) { DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw")); - return DUK_RET_RANGE_ERROR; + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); } - duk_push_array(ctx); + duk_push_array(thr); /* stack[0] = start * stack[1] = deleteCount @@ -20814,19 +26356,19 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * stack[nargs+2] = result array -1 */ - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); /* Step 9: copy elements-to-be-deleted into the result array */ for (i = 0; i < del_count; i++) { - if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (act_start + i))) { - duk_xdef_prop_index_wec(ctx, -2, i); /* throw flag irrelevant (false in std alg) */ + if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (act_start + i))) { + duk_xdef_prop_index_wec(thr, -2, (duk_uarridx_t) i); /* throw flag irrelevant (false in std alg) */ } else { - duk_pop(ctx); + duk_pop_undefined(thr); } } - duk_push_u32(ctx, (duk_uint32_t) del_count); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + duk_push_u32(thr, (duk_uint32_t) del_count); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); /* Steps 12 and 13: reorganize elements to make room for itemCount elements */ @@ -20837,27 +26379,27 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ A B C F G H ] (actual result at this point, C will be replaced) */ - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); n = len - del_count; for (i = act_start; i < n; i++) { - if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) { - duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count)); + if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) { + duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count)); } else { - duk_pop(ctx); - duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); + duk_pop_undefined(thr); + duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); } } - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); /* loop iterator init and limit changed from standard algorithm */ n = len - del_count + item_count; for (i = len - 1; i >= n; i--) { - duk_del_prop_index(ctx, -3, (duk_uarridx_t) i); + duk_del_prop_index(thr, -3, (duk_uarridx_t) i); } - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); } else if (item_count > del_count) { /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 4 * -> [ A B F G H ] (conceptual intermediate step) @@ -20865,19 +26407,19 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ A B C D E F F G H ] (actual result at this point) */ - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); /* loop iterator init and limit changed from standard algorithm */ for (i = len - del_count - 1; i >= act_start; i--) { - if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) { - duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count)); + if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) { + duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count)); } else { - duk_pop(ctx); - duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); + duk_pop_undefined(thr); + duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); } } - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); } else { /* [ A B C D E F G H ] rel_index = 2, del_count 3, item count 3 * -> [ A B F G H ] (conceptual intermediate step) @@ -20885,24 +26427,24 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ A B C D E F G H ] (actual result at this point) */ } - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); /* Step 15: insert itemCount elements into the hole made above */ for (i = 0; i < item_count; i++) { - duk_dup(ctx, i + 2); /* args start at index 2 */ - duk_put_prop_index(ctx, -4, (duk_uarridx_t) (act_start + i)); + duk_dup(thr, i + 2); /* args start at index 2 */ + duk_put_prop_index(thr, -4, (duk_uarridx_t) (act_start + i)); } /* Step 16: update length; note that the final length may be above 32 bit range * (but we checked above that this isn't the case here) */ - duk_push_u32(ctx, len - del_count + item_count); - duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH); + duk_push_u32(thr, (duk_uint32_t) (len - del_count + item_count)); + duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); /* result array is already at the top of stack */ - DUK_ASSERT_TOP(ctx, nargs + 3); + DUK_ASSERT_TOP(thr, nargs + 3); return 1; } @@ -20910,13 +26452,13 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * reverse() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_hthread *thr) { duk_uint32_t len; duk_uint32_t middle; duk_uint32_t lower, upper; duk_bool_t have_lower, have_upper; - len = duk__push_this_obj_len_u32(ctx); + len = duk__push_this_obj_len_u32(thr); middle = len / 2; /* If len <= 1, middle will be 0 and for-loop bails out @@ -20925,35 +26467,35 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) { for (lower = 0; lower < middle; lower++) { DUK_ASSERT(len >= 2); - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); DUK_ASSERT(len >= lower + 1); upper = len - lower - 1; - have_lower = duk_get_prop_index(ctx, -2, (duk_uarridx_t) lower); - have_upper = duk_get_prop_index(ctx, -3, (duk_uarridx_t) upper); + have_lower = duk_get_prop_index(thr, -2, (duk_uarridx_t) lower); + have_upper = duk_get_prop_index(thr, -3, (duk_uarridx_t) upper); /* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */ if (have_upper) { - duk_put_prop_index(ctx, -4, (duk_uarridx_t) lower); + duk_put_prop_index(thr, -4, (duk_uarridx_t) lower); } else { - duk_del_prop_index(ctx, -4, (duk_uarridx_t) lower); - duk_pop(ctx); + duk_del_prop_index(thr, -4, (duk_uarridx_t) lower); + duk_pop_undefined(thr); } if (have_lower) { - duk_put_prop_index(ctx, -3, (duk_uarridx_t) upper); + duk_put_prop_index(thr, -3, (duk_uarridx_t) upper); } else { - duk_del_prop_index(ctx, -3, (duk_uarridx_t) upper); - duk_pop(ctx); + duk_del_prop_index(thr, -3, (duk_uarridx_t) upper); + duk_pop_undefined(thr); } - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); } - DUK_ASSERT_TOP(ctx, 2); - duk_pop(ctx); /* -> [ ToObject(this) ] */ + DUK_ASSERT_TOP(thr, 2); + duk_pop_unsafe(thr); /* -> [ ToObject(this) ] */ return 1; } @@ -20961,8 +26503,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) { * slice() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { - duk_uint32_t len; +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_hthread *thr) { + duk_uint32_t len_u32; + duk_int_t len; duk_int_t start, end; duk_int_t i; duk_uarridx_t idx; @@ -20971,8 +26514,11 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { /* XXX: len >= 0x80000000 won't work below because we need to be * able to represent -len. */ - len = duk__push_this_obj_len_u32_limited(ctx); - duk_push_array(ctx); + len_u32 = duk__push_this_obj_len_u32_limited(thr); + len = (duk_int_t) len_u32; + DUK_ASSERT(len >= 0); + + duk_push_array(thr); /* stack[0] = start * stack[1] = end @@ -20981,41 +26527,41 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { * stack[4] = result array */ - start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len); + start = duk_to_int_clamped(thr, 0, -len, len); if (start < 0) { start = len + start; } /* XXX: could duk_is_undefined() provide defaulting undefined to 'len' * (the upper limit)? */ - if (duk_is_undefined(ctx, 1)) { + if (duk_is_undefined(thr, 1)) { end = len; } else { - end = duk_to_int_clamped(ctx, 1, -((duk_int_t) len), (duk_int_t) len); + end = duk_to_int_clamped(thr, 1, -len, len); if (end < 0) { end = len + end; } } - DUK_ASSERT(start >= 0 && (duk_uint32_t) start <= len); - DUK_ASSERT(end >= 0 && (duk_uint32_t) end <= len); + DUK_ASSERT(start >= 0 && start <= len); + DUK_ASSERT(end >= 0 && end <= len); idx = 0; for (i = start; i < end; i++) { - DUK_ASSERT_TOP(ctx, 5); - if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) { - duk_xdef_prop_index_wec(ctx, 4, idx); + DUK_ASSERT_TOP(thr, 5); + if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { + duk_xdef_prop_index_wec(thr, 4, idx); res_length = idx + 1; } else { - duk_pop(ctx); + duk_pop_undefined(thr); } idx++; - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); } - duk_push_u32(ctx, res_length); - duk_xdef_prop_stridx(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + duk_push_u32(thr, res_length); + duk_xdef_prop_stridx_short(thr, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); return 1; } @@ -21023,18 +26569,18 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { * shift() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_hthread *thr) { duk_uint32_t len; duk_uint32_t i; - len = duk__push_this_obj_len_u32(ctx); + len = duk__push_this_obj_len_u32(thr); if (len == 0) { - duk_push_int(ctx, 0); - duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH); + duk_push_int(thr, 0); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); return 0; } - duk_get_prop_index(ctx, 0, 0); + duk_get_prop_index(thr, 0, 0); /* stack[0] = object (this) * stack[1] = ToUint32(length) @@ -21042,22 +26588,22 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) { */ for (i = 1; i < len; i++) { - DUK_ASSERT_TOP(ctx, 3); - if (duk_get_prop_index(ctx, 0, (duk_uarridx_t) i)) { + DUK_ASSERT_TOP(thr, 3); + if (duk_get_prop_index(thr, 0, (duk_uarridx_t) i)) { /* fromPresent = true */ - duk_put_prop_index(ctx, 0, (duk_uarridx_t) (i - 1)); + duk_put_prop_index(thr, 0, (duk_uarridx_t) (i - 1)); } else { /* fromPresent = false */ - duk_del_prop_index(ctx, 0, (duk_uarridx_t) (i - 1)); - duk_pop(ctx); + duk_del_prop_index(thr, 0, (duk_uarridx_t) (i - 1)); + duk_pop_undefined(thr); } } - duk_del_prop_index(ctx, 0, (duk_uarridx_t) (len - 1)); + duk_del_prop_index(thr, 0, (duk_uarridx_t) (len - 1)); - duk_push_u32(ctx, (duk_uint32_t) (len - 1)); - duk_put_prop_stridx(ctx, 0, DUK_STRIDX_LENGTH); + duk_push_u32(thr, (duk_uint32_t) (len - 1)); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - DUK_ASSERT_TOP(ctx, 3); + DUK_ASSERT_TOP(thr, 3); return 1; } @@ -21065,20 +26611,20 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) { * unshift() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_hthread *thr) { duk_idx_t nargs; duk_uint32_t len; duk_uint32_t i; - nargs = duk_get_top(ctx); - len = duk__push_this_obj_len_u32(ctx); + nargs = duk_get_top(thr); + len = duk__push_this_obj_len_u32(thr); /* stack[0...nargs-1] = unshift args (vararg) * stack[nargs] = ToObject(this) * stack[nargs+1] = ToUint32(length) */ - DUK_ASSERT_TOP(ctx, nargs + 2); + DUK_ASSERT_TOP(thr, nargs + 2); /* Note: unshift() may operate on indices above unsigned 32-bit range * and the final length may be >= 2**32. However, we restrict the @@ -21087,39 +26633,39 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) { if (len + (duk_uint32_t) nargs < len) { DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw")); - return DUK_RET_RANGE_ERROR; + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); } i = len; while (i > 0) { - DUK_ASSERT_TOP(ctx, nargs + 2); + DUK_ASSERT_TOP(thr, nargs + 2); i--; /* k+argCount-1; note that may be above 32-bit range */ - if (duk_get_prop_index(ctx, -2, (duk_uarridx_t) i)) { + if (duk_get_prop_index(thr, -2, (duk_uarridx_t) i)) { /* fromPresent = true */ /* [ ... ToObject(this) ToUint32(length) val ] */ - duk_put_prop_index(ctx, -3, (duk_uarridx_t) (i + nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ + duk_put_prop_index(thr, -3, (duk_uarridx_t) (i + (duk_uint32_t) nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ } else { /* fromPresent = false */ /* [ ... ToObject(this) ToUint32(length) val ] */ - duk_pop(ctx); - duk_del_prop_index(ctx, -2, (duk_uarridx_t) (i + nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ + duk_pop_undefined(thr); + duk_del_prop_index(thr, -2, (duk_uarridx_t) (i + (duk_uint32_t) nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ } - DUK_ASSERT_TOP(ctx, nargs + 2); + DUK_ASSERT_TOP(thr, nargs + 2); } for (i = 0; i < (duk_uint32_t) nargs; i++) { - DUK_ASSERT_TOP(ctx, nargs + 2); - duk_dup(ctx, i); /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */ - duk_put_prop_index(ctx, -3, (duk_uarridx_t) i); - DUK_ASSERT_TOP(ctx, nargs + 2); + DUK_ASSERT_TOP(thr, nargs + 2); + duk_dup(thr, (duk_idx_t) i); /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */ + duk_put_prop_index(thr, -3, (duk_uarridx_t) i); + DUK_ASSERT_TOP(thr, nargs + 2); } - DUK_ASSERT_TOP(ctx, nargs + 2); - duk_push_u32(ctx, len + nargs); - duk_dup_top(ctx); /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */ - duk_put_prop_stridx(ctx, -4, DUK_STRIDX_LENGTH); + DUK_ASSERT_TOP(thr, nargs + 2); + duk_push_u32(thr, len + (duk_uint32_t) nargs); + duk_dup_top(thr); /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */ + duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH); return 1; } @@ -21127,22 +26673,22 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) { * indexOf(), lastIndexOf() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_hthread *thr) { duk_idx_t nargs; duk_int_t i, len; - duk_int_t from_index; - duk_small_int_t idx_step = duk_get_current_magic(ctx); /* idx_step is +1 for indexOf, -1 for lastIndexOf */ + duk_int_t from_idx; + duk_small_int_t idx_step = duk_get_current_magic(thr); /* idx_step is +1 for indexOf, -1 for lastIndexOf */ /* lastIndexOf() needs to be a vararg function because we must distinguish * between an undefined fromIndex and a "not given" fromIndex; indexOf() is * made vararg for symmetry although it doesn't strictly need to be. */ - nargs = duk_get_top(ctx); - duk_set_top(ctx, 2); + nargs = duk_get_top(thr); + duk_set_top(thr, 2); /* XXX: must be able to represent -len */ - len = (duk_int_t) duk__push_this_obj_len_u32_limited(ctx); + len = (duk_int_t) duk__push_this_obj_len_u32_limited(thr); if (len == 0) { goto not_found; } @@ -21169,22 +26715,22 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { * lastIndexOf: clamp fromIndex to [-len - 1, len - 1] * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly) */ - from_index = duk_to_int_clamped(ctx, - 1, - (idx_step > 0 ? -len : -len - 1), - (idx_step > 0 ? len : len - 1)); - if (from_index < 0) { + from_idx = duk_to_int_clamped(thr, + 1, + (idx_step > 0 ? -len : -len - 1), + (idx_step > 0 ? len : len - 1)); + if (from_idx < 0) { /* for lastIndexOf, result may be -1 (mark immediate termination) */ - from_index = len + from_index; + from_idx = len + from_idx; } } else { /* for indexOf, ToInteger(undefined) would be 0, i.e. correct, but * handle both indexOf and lastIndexOf specially here. */ if (idx_step > 0) { - from_index = 0; + from_idx = 0; } else { - from_index = len - 1; + from_idx = len - 1; } } @@ -21194,22 +26740,22 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { * stack[3] = length (not needed, but not popped above) */ - for (i = from_index; i >= 0 && i < len; i += idx_step) { - DUK_ASSERT_TOP(ctx, 4); + for (i = from_idx; i >= 0 && i < len; i += idx_step) { + DUK_ASSERT_TOP(thr, 4); - if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) { - DUK_ASSERT_TOP(ctx, 5); - if (duk_strict_equals(ctx, 0, 4)) { - duk_push_int(ctx, i); + if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { + DUK_ASSERT_TOP(thr, 5); + if (duk_strict_equals(thr, 0, 4)) { + duk_push_int(thr, i); return 1; } } - duk_pop(ctx); + duk_pop_unsafe(thr); } not_found: - duk_push_int(ctx, -1); + duk_push_int(thr, -1); return 1; } @@ -21228,25 +26774,25 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { * 5 callers the net result is about 100 bytes / caller. */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_hthread *thr) { duk_uint32_t len; duk_uint32_t i; duk_uarridx_t k; duk_bool_t bval; - duk_small_int_t iter_type = duk_get_current_magic(ctx); + duk_small_int_t iter_type = duk_get_current_magic(thr); duk_uint32_t res_length = 0; /* each call this helper serves has nargs==2 */ - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); - len = duk__push_this_obj_len_u32(ctx); - duk_require_callable(ctx, 0); + len = duk__push_this_obj_len_u32(thr); + duk_require_callable(thr, 0); /* if thisArg not supplied, behave as if undefined was supplied */ if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) { - duk_push_array(ctx); + duk_push_array(thr); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } /* stack[0] = callback @@ -21258,24 +26804,16 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { k = 0; /* result index for filter() */ for (i = 0; i < len; i++) { - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); - if (!duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) { -#if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER) - /* Real world behavior for map(): trailing non-existent - * elements don't invoke the user callback, but are still - * counted towards result 'length'. + if (!duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) { + /* For 'map' trailing missing elements don't invoke the + * callback but count towards the result length. */ if (iter_type == DUK__ITER_MAP) { res_length = i + 1; } -#else - /* Standard behavior for map(): trailing non-existent - * elements don't invoke the user callback and are not - * counted towards result 'length'. - */ -#endif - duk_pop(ctx); + duk_pop_undefined(thr); continue; } @@ -21284,23 +26822,23 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { * effects. */ - duk_dup(ctx, 0); - duk_dup(ctx, 1); - duk_dup(ctx, -3); - duk_push_u32(ctx, i); - duk_dup(ctx, 2); /* [ ... val callback thisArg val i obj ] */ - duk_call_method(ctx, 3); /* -> [ ... val retval ] */ + duk_dup_0(thr); + duk_dup_1(thr); + duk_dup_m3(thr); + duk_push_u32(thr, i); + duk_dup_2(thr); /* [ ... val callback thisArg val i obj ] */ + duk_call_method(thr, 3); /* -> [ ... val retval ] */ switch (iter_type) { case DUK__ITER_EVERY: - bval = duk_to_boolean(ctx, -1); + bval = duk_to_boolean(thr, -1); if (!bval) { /* stack top contains 'false' */ return 1; } break; case DUK__ITER_SOME: - bval = duk_to_boolean(ctx, -1); + bval = duk_to_boolean(thr, -1); if (bval) { /* stack top contains 'true' */ return 1; @@ -21310,15 +26848,15 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { /* nop */ break; case DUK__ITER_MAP: - duk_dup(ctx, -1); - duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) i); /* retval to result[i] */ + duk_dup_top(thr); + duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) i); /* retval to result[i] */ res_length = i + 1; break; case DUK__ITER_FILTER: - bval = duk_to_boolean(ctx, -1); + bval = duk_to_boolean(thr, -1); if (bval) { - duk_dup(ctx, -2); /* orig value */ - duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) k); + duk_dup_m2(thr); /* orig value */ + duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) k); k++; res_length = k; } @@ -21327,27 +26865,27 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { DUK_UNREACHABLE(); break; } - duk_pop_2(ctx); + duk_pop_2_unsafe(thr); - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); } switch (iter_type) { case DUK__ITER_EVERY: - duk_push_true(ctx); + duk_push_true(thr); break; case DUK__ITER_SOME: - duk_push_false(ctx); + duk_push_false(thr); break; case DUK__ITER_FOREACH: - duk_push_undefined(ctx); + duk_push_undefined(thr); break; case DUK__ITER_MAP: case DUK__ITER_FILTER: - DUK_ASSERT_TOP(ctx, 5); - DUK_ASSERT(duk_is_array(ctx, -1)); /* topmost element is the result array already */ - duk_push_u32(ctx, res_length); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + DUK_ASSERT_TOP(thr, 5); + DUK_ASSERT(duk_is_array(thr, -1)); /* topmost element is the result array already */ + duk_push_u32(thr, res_length); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); break; default: DUK_UNREACHABLE(); @@ -21361,23 +26899,21 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { * reduce(), reduceRight() */ -DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_hthread *thr) { duk_idx_t nargs; duk_bool_t have_acc; duk_uint32_t i, len; - duk_small_int_t idx_step = duk_get_current_magic(ctx); /* idx_step is +1 for reduce, -1 for reduceRight */ + duk_small_int_t idx_step = duk_get_current_magic(thr); /* idx_step is +1 for reduce, -1 for reduceRight */ /* We're a varargs function because we need to detect whether * initialValue was given or not. */ - nargs = duk_get_top(ctx); + nargs = duk_get_top(thr); DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs)); - duk_set_top(ctx, 2); - len = duk__push_this_obj_len_u32(ctx); - if (!duk_is_callable(ctx, 0)) { - goto type_error; - } + duk_set_top(thr, 2); + len = duk__push_this_obj_len_u32(thr); + duk_require_callable(thr, 0); /* stack[0] = callback fn * stack[1] = initialValue @@ -21388,11 +26924,11 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { have_acc = 0; if (nargs >= 2) { - duk_dup(ctx, 1); + duk_dup_1(thr); have_acc = 1; } DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T", - (long) have_acc, (duk_tval *) duk_get_tval(ctx, 3))); + (long) have_acc, (duk_tval *) duk_get_tval(thr, 3))); /* For len == 0, i is initialized to len - 1 which underflows. * The condition (i < len) will then exit the for-loop on the @@ -21402,82 +26938,82 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { for (i = (idx_step >= 0 ? 0 : len - 1); i < len; /* i >= 0 would always be true */ - i += idx_step) { + i += (duk_uint32_t) idx_step) { DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T", (long) i, (long) len, (long) have_acc, - (long) duk_get_top(ctx), - (duk_tval *) duk_get_tval(ctx, 4))); + (long) duk_get_top(thr), + (duk_tval *) duk_get_tval(thr, 4))); - DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) || - (!have_acc && duk_get_top(ctx) == 4)); + DUK_ASSERT((have_acc && duk_get_top(thr) == 5) || + (!have_acc && duk_get_top(thr) == 4)); - if (!duk_has_prop_index(ctx, 2, (duk_uarridx_t) i)) { + if (!duk_has_prop_index(thr, 2, (duk_uarridx_t) i)) { continue; } if (!have_acc) { - DUK_ASSERT_TOP(ctx, 4); - duk_get_prop_index(ctx, 2, (duk_uarridx_t) i); + DUK_ASSERT_TOP(thr, 4); + duk_get_prop_index(thr, 2, (duk_uarridx_t) i); have_acc = 1; - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); } else { - DUK_ASSERT_TOP(ctx, 5); - duk_dup(ctx, 0); - duk_dup(ctx, 4); - duk_get_prop_index(ctx, 2, (duk_uarridx_t) i); - duk_push_u32(ctx, i); - duk_dup(ctx, 2); + DUK_ASSERT_TOP(thr, 5); + duk_dup_0(thr); + duk_dup(thr, 4); + duk_get_prop_index(thr, 2, (duk_uarridx_t) i); + duk_push_u32(thr, i); + duk_dup_2(thr); DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T", - (duk_tval *) duk_get_tval(ctx, -5), (duk_tval *) duk_get_tval(ctx, -4), - (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); - duk_call(ctx, 4); - DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(ctx, -1))); - duk_replace(ctx, 4); - DUK_ASSERT_TOP(ctx, 5); + (duk_tval *) duk_get_tval(thr, -5), (duk_tval *) duk_get_tval(thr, -4), + (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); + duk_call(thr, 4); + DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(thr, -1))); + duk_replace(thr, 4); + DUK_ASSERT_TOP(thr, 5); } } if (!have_acc) { - goto type_error; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); return 1; - - type_error: - return DUK_RET_TYPE_ERROR; } -#undef DUK__ARRAY_MID_JOIN_LIMIT +#endif /* DUK_USE_ARRAY_BUILTIN */ +/* automatic undefs */ +#undef DUK__ARRAY_MID_JOIN_LIMIT #undef DUK__ITER_EVERY -#undef DUK__ITER_SOME +#undef DUK__ITER_FILTER #undef DUK__ITER_FOREACH #undef DUK__ITER_MAP -#undef DUK__ITER_FILTER -#line 1 "duk_bi_boolean.c" +#undef DUK__ITER_SOME /* * Boolean built-ins */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +#if defined(DUK_USE_BOOLEAN_BUILTIN) /* Shared helper to provide toString() and valueOf(). Checks 'this', gets * the primitive value to stack top, and optionally coerces with ToString(). */ -DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_hthread *thr) { duk_tval *tv; duk_hobject *h; - duk_small_int_t coerce_tostring = duk_get_current_magic(ctx); + duk_small_int_t coerce_tostring = duk_get_current_magic(thr); /* XXX: there is room to use a shared helper here, many built-ins * check the 'this' type, and if it's an object, check its class, * then get its internal value, etc. */ - duk_push_this(ctx); - tv = duk_get_tval(ctx, -1); + duk_push_this(thr); + tv = duk_get_tval(thr, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_BOOLEAN(tv)) { @@ -21487,58 +27023,73 @@ DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx DUK_ASSERT(h != NULL); if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_BOOLEAN) { - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); - DUK_ASSERT(duk_is_boolean(ctx, -1)); + duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); + DUK_ASSERT(duk_is_boolean(thr, -1)); goto type_ok; } } - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); + /* never here */ type_ok: if (coerce_tostring) { - duk_to_string(ctx, -1); + duk_to_string(thr, -1); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_hthread *thr) { duk_hobject *h_this; - DUK_UNREF(thr); + duk_to_boolean(thr, 0); - duk_to_boolean(ctx, 0); - - if (duk_is_constructor_call(ctx)) { + if (duk_is_constructor_call(thr)) { /* XXX: helper; rely on Boolean.prototype as being non-writable, non-configurable */ - duk_push_this(ctx); - h_this = duk_get_hobject(ctx, -1); - DUK_ASSERT(h_this != NULL); + duk_push_this(thr); + h_this = duk_known_hobject(thr, -1); DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]); DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN); - duk_dup(ctx, 0); /* -> [ val obj val ] */ - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); /* XXX: proper flags? */ + duk_dup_0(thr); /* -> [ val obj val ] */ + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE); /* XXX: proper flags? */ } /* unbalanced stack */ return 1; } -#line 1 "duk_bi_buffer.c" + +#endif /* DUK_USE_BOOLEAN_BUILTIN */ /* - * Duktape.Buffer, Node.js Buffer, and Khronos/ES6 TypedArray built-ins + * ES2015 TypedArray and Node.js Buffer built-ins */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* - * Misc helpers + * Helpers for buffer handling, enabled with DUK_USE_BUFFEROBJECT_SUPPORT. */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Map DUK_HBUFFEROBJECT_ELEM_xxx to duk_hobject class number. - * Sync with duk_hbufferobject.h and duk_hobject.h. +/* Map class number (minus DUK_HOBJECT_CLASS_BUFOBJ_MIN) to a bidx for the + * default internal prototype. + */ +static const duk_uint8_t duk__buffer_proto_from_classnum[] = { + DUK_BIDX_ARRAYBUFFER_PROTOTYPE, + DUK_BIDX_DATAVIEW_PROTOTYPE, + DUK_BIDX_INT8ARRAY_PROTOTYPE, + DUK_BIDX_UINT8ARRAY_PROTOTYPE, + DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, + DUK_BIDX_INT16ARRAY_PROTOTYPE, + DUK_BIDX_UINT16ARRAY_PROTOTYPE, + DUK_BIDX_INT32ARRAY_PROTOTYPE, + DUK_BIDX_UINT32ARRAY_PROTOTYPE, + DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, + DUK_BIDX_FLOAT64ARRAY_PROTOTYPE +}; + +/* Map DUK_HBUFOBJ_ELEM_xxx to duk_hobject class number. + * Sync with duk_hbufobj.h and duk_hobject.h. */ static const duk_uint8_t duk__buffer_class_from_elemtype[9] = { DUK_HOBJECT_CLASS_UINT8ARRAY, @@ -21551,11 +27102,9 @@ static const duk_uint8_t duk__buffer_class_from_elemtype[9] = { DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_HOBJECT_CLASS_FLOAT64ARRAY }; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Map DUK_HBUFFEROBJECT_ELEM_xxx to prototype object built-in index. - * Sync with duk_hbufferobject.h. +/* Map DUK_HBUFOBJ_ELEM_xxx to prototype object built-in index. + * Sync with duk_hbufobj.h. */ static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = { DUK_BIDX_UINT8ARRAY_PROTOTYPE, @@ -21568,11 +27117,8 @@ static const duk_uint8_t duk__buffer_proto_from_elemtype[9] = { DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE }; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Map DUK__FLX_xxx to byte size. - */ +/* Map DUK__FLD_xxx to byte size. */ static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = { 1, /* DUK__FLD_8BIT */ 2, /* DUK__FLD_16BIT */ @@ -21581,191 +27127,193 @@ static const duk_uint8_t duk__buffer_nbytes_from_fldtype[6] = { 8, /* DUK__FLD_DOUBLE */ 0 /* DUK__FLD_VARINT; not relevant here */ }; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Bitfield for each DUK_HBUFFEROBJECT_ELEM_xxx indicating which element types +/* Bitfield for each DUK_HBUFOBJ_ELEM_xxx indicating which element types * are compatible with a blind byte copy for the TypedArray set() method (also * used for TypedArray constructor). Array index is target buffer elem type, * bitfield indicates compatible source types. The types must have same byte * size and they must be coercion compatible. */ +#if !defined(DUK_USE_PREFER_SIZE) static duk_uint16_t duk__buffer_elemtype_copy_compatible[9] = { - /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) | - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT8), + /* xxx -> DUK_HBUFOBJ_ELEM_UINT8 */ + (1U << DUK_HBUFOBJ_ELEM_UINT8) | + (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) | + (1U << DUK_HBUFOBJ_ELEM_INT8), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED + /* xxx -> DUK_HBUFOBJ_ELEM_UINT8CLAMPED * Note: INT8 is -not- copy compatible, e.g. -1 would coerce to 0x00. */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) | - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED), + (1U << DUK_HBUFOBJ_ELEM_UINT8) | + (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT8 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8) | - (1U << DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT8), + /* xxx -> DUK_HBUFOBJ_ELEM_INT8 */ + (1U << DUK_HBUFOBJ_ELEM_UINT8) | + (1U << DUK_HBUFOBJ_ELEM_UINT8CLAMPED) | + (1U << DUK_HBUFOBJ_ELEM_INT8), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT16 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT16) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT16), + /* xxx -> DUK_HBUFOBJ_ELEM_UINT16 */ + (1U << DUK_HBUFOBJ_ELEM_UINT16) | + (1U << DUK_HBUFOBJ_ELEM_INT16), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT16 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT16) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT16), + /* xxx -> DUK_HBUFOBJ_ELEM_INT16 */ + (1U << DUK_HBUFOBJ_ELEM_UINT16) | + (1U << DUK_HBUFOBJ_ELEM_INT16), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_UINT32 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT32) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT32), + /* xxx -> DUK_HBUFOBJ_ELEM_UINT32 */ + (1U << DUK_HBUFOBJ_ELEM_UINT32) | + (1U << DUK_HBUFOBJ_ELEM_INT32), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_INT32 */ - (1U << DUK_HBUFFEROBJECT_ELEM_UINT32) | - (1U << DUK_HBUFFEROBJECT_ELEM_INT32), + /* xxx -> DUK_HBUFOBJ_ELEM_INT32 */ + (1U << DUK_HBUFOBJ_ELEM_UINT32) | + (1U << DUK_HBUFOBJ_ELEM_INT32), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT32 */ - (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT32), + /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT32 */ + (1U << DUK_HBUFOBJ_ELEM_FLOAT32), - /* xxx -> DUK_HBUFFEROBJECT_ELEM_FLOAT64 */ - (1U << DUK_HBUFFEROBJECT_ELEM_FLOAT64) + /* xxx -> DUK_HBUFOBJ_ELEM_FLOAT64 */ + (1U << DUK_HBUFOBJ_ELEM_FLOAT64) }; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ +#endif /* !DUK_USE_PREFER_SIZE */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Shared helper. */ -DUK_LOCAL duk_hbufferobject *duk__getrequire_bufobj_this(duk_context *ctx, duk_bool_t throw_flag) { - duk_hthread *thr; +DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_hthread *thr) { + duk_tval *tv_dst; + duk_hbufobj *res; + + duk_push_this(thr); + DUK_ASSERT(duk_is_buffer(thr, -1)); + res = (duk_hbufobj *) duk_to_hobject(thr, -1); + DUK_HBUFOBJ_ASSERT_VALID(res); + DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(thr, -1))); + + tv_dst = duk_get_borrowed_this_tval(thr); + DUK_TVAL_SET_OBJECT_UPDREF(thr, tv_dst, (duk_hobject *) res); + duk_pop(thr); + + return res; +} + +#define DUK__BUFOBJ_FLAG_THROW (1 << 0) +#define DUK__BUFOBJ_FLAG_PROMOTE (1 << 1) + +/* Shared helper. When DUK__BUFOBJ_FLAG_PROMOTE is given, the return value is + * always a duk_hbufobj *. Without the flag the return value can also be a + * plain buffer, and the caller must check for it using DUK_HEAPHDR_IS_BUFFER(). + */ +DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_hthread *thr, duk_small_uint_t flags) { duk_tval *tv; - duk_hbufferobject *h_this; + duk_hbufobj *h_this; - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; + DUK_ASSERT(thr != NULL); - tv = duk_get_borrowed_this_tval(ctx); + tv = duk_get_borrowed_this_tval(thr); DUK_ASSERT(tv != NULL); + if (DUK_TVAL_IS_OBJECT(tv)) { - h_this = (duk_hbufferobject *) DUK_TVAL_GET_OBJECT(tv); + h_this = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h_this != NULL); - if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_this)) { - DUK_ASSERT_HBUFFEROBJECT_VALID(h_this); - return h_this; + if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_this)) { + DUK_HBUFOBJ_ASSERT_VALID(h_this); + return (duk_heaphdr *) h_this; + } + } else if (DUK_TVAL_IS_BUFFER(tv)) { + if (flags & DUK__BUFOBJ_FLAG_PROMOTE) { + /* Promote a plain buffer to a Uint8Array. This is very + * inefficient but allows plain buffer to be used wherever an + * Uint8Array is used with very small cost; hot path functions + * like index read/write calls should provide direct buffer + * support to avoid promotion. + */ + /* XXX: make this conditional to a flag if call sites need it? */ + h_this = duk__hbufobj_promote_this(thr); + DUK_ASSERT(h_this != NULL); + DUK_HBUFOBJ_ASSERT_VALID(h_this); + return (duk_heaphdr *) h_this; + } else { + /* XXX: ugly, share return pointer for duk_hbuffer. */ + return (duk_heaphdr *) DUK_TVAL_GET_BUFFER(tv); } } - if (throw_flag) { + if (flags & DUK__BUFOBJ_FLAG_THROW) { DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER); + DUK_WO_NORETURN(return NULL;); } return NULL; } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Check that 'this' is a duk_hbufferobject and return a pointer to it. */ -DUK_LOCAL duk_hbufferobject *duk__get_bufobj_this(duk_context *ctx) { - return duk__getrequire_bufobj_this(ctx, 0); +/* Check that 'this' is a duk_hbufobj and return a pointer to it. */ +DUK_LOCAL duk_hbufobj *duk__get_bufobj_this(duk_hthread *thr) { + return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_PROMOTE); } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Check that 'this' is a duk_hbufferobject and return a pointer to it +/* Check that 'this' is a duk_hbufobj and return a pointer to it * (NULL if not). */ -DUK_LOCAL duk_hbufferobject *duk__require_bufobj_this(duk_context *ctx) { - return duk__getrequire_bufobj_this(ctx, 1); +DUK_LOCAL duk_hbufobj *duk__require_bufobj_this(duk_hthread *thr) { + return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW | DUK__BUFOBJ_FLAG_PROMOTE); } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -/* Check that value is a duk_hbufferobject and return a pointer to it. */ -DUK_LOCAL duk_hbufferobject *duk__require_bufobj_value(duk_context *ctx, duk_idx_t index) { - duk_hthread *thr; +/* Check that value is a duk_hbufobj and return a pointer to it. */ +DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - duk_hbufferobject *h_obj; - - thr = (duk_hthread *) ctx; + duk_hbufobj *h_obj; /* Don't accept relative indices now. */ - DUK_ASSERT(index >= 0); + DUK_ASSERT(idx >= 0); - tv = duk_require_tval(ctx, index); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_OBJECT(tv)) { - h_obj = (duk_hbufferobject *) DUK_TVAL_GET_OBJECT(tv); + h_obj = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h_obj != NULL); - if (DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_obj)) { - DUK_ASSERT_HBUFFEROBJECT_VALID(h_obj); + if (DUK_HOBJECT_IS_BUFOBJ((duk_hobject *) h_obj)) { + DUK_HBUFOBJ_ASSERT_VALID(h_obj); return h_obj; } + } else if (DUK_TVAL_IS_BUFFER(tv)) { + h_obj = (duk_hbufobj *) duk_to_hobject(thr, idx); + DUK_ASSERT(h_obj != NULL); + DUK_HBUFOBJ_ASSERT_VALID(h_obj); + return h_obj; } DUK_ERROR_TYPE(thr, DUK_STR_NOT_BUFFER); - return NULL; /* not reachable */ + DUK_WO_NORETURN(return NULL;); } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_hbuffer *h_val) { - duk_hthread *thr; - - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - DUK_ASSERT(ctx != NULL); +DUK_LOCAL void duk__set_bufobj_buffer(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_hbuffer *h_val) { + DUK_ASSERT(thr != NULL); DUK_ASSERT(h_bufobj != NULL); DUK_ASSERT(h_bufobj->buf == NULL); /* no need to decref */ DUK_ASSERT(h_val != NULL); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); + DUK_UNREF(thr); h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val); DUK_ASSERT(h_bufobj->shift == 0); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8); + DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8); + DUK_ASSERT(h_bufobj->is_typedarray == 0); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); } -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_LOCAL duk_hbufferobject *duk__push_arraybuffer_with_length(duk_context *ctx, duk_uint_t len) { - duk_hbuffer *h_val; - duk_hbufferobject *h_bufobj; - - (void) duk_push_fixed_buffer(ctx, (duk_size_t) len); - h_val = (duk_hbuffer *) duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_val != NULL); - - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); - - duk__set_bufobj_buffer(ctx, h_bufobj, h_val); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - - return h_bufobj; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* Shared offset/length coercion helper. */ -DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, - duk_hbufferobject *h_bufarg, +DUK_LOCAL void duk__resolve_offset_opt_length(duk_hthread *thr, + duk_hbufobj *h_bufarg, duk_idx_t idx_offset, duk_idx_t idx_length, duk_uint_t *out_offset, duk_uint_t *out_length, duk_bool_t throw_flag) { - duk_hthread *thr; duk_int_t offset_signed; duk_int_t length_signed; duk_uint_t offset; duk_uint_t length; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - offset_signed = duk_to_int(ctx, idx_offset); + offset_signed = duk_to_int(thr, idx_offset); if (offset_signed < 0) { goto fail_range; } @@ -21776,11 +27324,11 @@ DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, DUK_ASSERT_DISABLE(offset >= 0); /* unsigned */ DUK_ASSERT(offset <= h_bufarg->length); - if (duk_is_undefined(ctx, idx_length)) { + if (duk_is_undefined(thr, idx_length)) { DUK_ASSERT(h_bufarg->length >= offset); length = h_bufarg->length - offset; /* >= 0 */ } else { - length_signed = duk_to_int(ctx, idx_length); + length_signed = duk_to_int(thr, idx_length); if (length_signed < 0) { goto fail_range; } @@ -21805,35 +27353,31 @@ DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, return; fail_range: - DUK_ERROR_RANGE(thr, DUK_STR_INVALID_CALL_ARGS); + DUK_ERROR_RANGE(thr, DUK_STR_INVALID_ARGS); + DUK_WO_NORETURN(return;); } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* Shared lenient buffer length clamping helper. No negative indices, no * element/byte shifting. */ -DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx, - duk_hbufferobject *h_bufobj, +DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_hthread *thr, + duk_int_t buffer_length, duk_idx_t idx_start, duk_idx_t idx_end, duk_int_t *out_start_offset, duk_int_t *out_end_offset) { - duk_int_t buffer_length; duk_int_t start_offset; duk_int_t end_offset; DUK_ASSERT(out_start_offset != NULL); DUK_ASSERT(out_end_offset != NULL); - buffer_length = (duk_int_t) h_bufobj->length; - /* undefined coerces to zero which is correct */ - start_offset = duk_to_int_clamped(ctx, idx_start, 0, buffer_length); - if (duk_is_undefined(ctx, idx_end)) { + start_offset = duk_to_int_clamped(thr, idx_start, 0, buffer_length); + if (duk_is_undefined(thr, idx_end)) { end_offset = buffer_length; } else { - end_offset = duk_to_int_clamped(ctx, idx_end, start_offset, buffer_length); + end_offset = duk_to_int_clamped(thr, idx_end, start_offset, buffer_length); } DUK_ASSERT(start_offset >= 0); @@ -21845,9 +27389,7 @@ DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx, *out_start_offset = start_offset; *out_end_offset = end_offset; } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* Shared lenient buffer length clamping helper. Indices are treated as * element indices (though output values are byte offsets) which only * really matters for TypedArray views as other buffer object have a zero @@ -21855,35 +27397,34 @@ DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx, * indices are clamped to zero length; and final indices are clamped * against input slice. Used for e.g. ArrayBuffer slice(). */ -DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx, - duk_hbufferobject *h_bufobj, +DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_hthread *thr, + duk_int_t buffer_length, + duk_uint8_t buffer_shift, duk_idx_t idx_start, duk_idx_t idx_end, duk_int_t *out_start_offset, duk_int_t *out_end_offset) { - duk_int_t buffer_length; duk_int_t start_offset; duk_int_t end_offset; DUK_ASSERT(out_start_offset != NULL); DUK_ASSERT(out_end_offset != NULL); - buffer_length = (duk_int_t) h_bufobj->length; - buffer_length >>= h_bufobj->shift; /* as elements */ + buffer_length >>= buffer_shift; /* as (full) elements */ /* Resolve start/end offset as element indices first; arguments * at idx_start/idx_end are element offsets. Working with element * indices first also avoids potential for wrapping. */ - start_offset = duk_to_int(ctx, idx_start); + start_offset = duk_to_int(thr, idx_start); if (start_offset < 0) { start_offset = buffer_length + start_offset; } - if (duk_is_undefined(ctx, idx_end)) { + if (duk_is_undefined(thr, idx_end)) { end_offset = buffer_length; } else { - end_offset = duk_to_int(ctx, idx_end); + end_offset = duk_to_int(thr, idx_end); if (end_offset < 0) { end_offset = buffer_length + end_offset; } @@ -21907,60 +27448,99 @@ DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx, DUK_ASSERT(start_offset <= end_offset); /* Convert indices to byte offsets. */ - start_offset <<= h_bufobj->shift; - end_offset <<= h_bufobj->shift; + start_offset <<= buffer_shift; + end_offset <<= buffer_shift; *out_start_offset = start_offset; *out_end_offset = end_offset; } -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* - * Indexed read/write helpers (also used from outside this file) - */ +DUK_INTERNAL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx) { + if (duk_is_buffer(thr, idx)) { + duk_to_object(thr, idx); + } +} -DUK_INTERNAL void duk_hbufferobject_push_validated_read(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { +DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf) { + /* Push Uint8Array which will share the same underlying buffer as + * the plain buffer argument. Also create an ArrayBuffer with the + * same backing for the result .buffer property. + */ + + duk_push_hbuffer(thr, h_buf); + duk_push_buffer_object(thr, -1, 0, (duk_size_t) DUK_HBUFFER_GET_SIZE(h_buf), DUK_BUFOBJ_UINT8ARRAY); + duk_remove_m2(thr); + +#if 0 + /* More verbose equivalent; maybe useful if e.g. .buffer is omitted. */ + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY), + DUK_BIDX_UINT8ARRAY_PROTOTYPE); + DUK_ASSERT(h_bufobj != NULL); + duk__set_bufobj_buffer(thr, h_bufobj, h_buf); + h_bufobj->is_typedarray = 1; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); + + h_arrbuf = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), + DUK_BIDX_ARRAYBUFFER_PROTOTYPE); + DUK_ASSERT(h_arrbuf != NULL); + duk__set_bufobj_buffer(thr, h_arrbuf, h_buf); + DUK_ASSERT(h_arrbuf->is_typedarray == 0); + DUK_HBUFOBJ_ASSERT_VALID(h_arrbuf); + + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; + DUK_ASSERT(h_arrbuf != NULL); + DUK_HBUFOBJ_INCREF(thr, h_arrbuf); + duk_pop(thr); +#endif +} + +/* Indexed read helper for buffer objects, also called from outside this file. */ +DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { duk_double_union du; - DUK_MEMCPY((void *) du.uc, (const void *) p, (size_t) elem_size); + DUK_ASSERT(elem_size > 0); + duk_memcpy((void *) du.uc, (const void *) p, (size_t) elem_size); switch (h_bufobj->elem_type) { - case DUK_HBUFFEROBJECT_ELEM_UINT8: -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED: -#endif - duk_push_uint(ctx, (duk_uint_t) du.uc[0]); + case DUK_HBUFOBJ_ELEM_UINT8: + case DUK_HBUFOBJ_ELEM_UINT8CLAMPED: + duk_push_uint(thr, (duk_uint_t) du.uc[0]); break; -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - /* These are not needed when only Duktape.Buffer is supported. */ - case DUK_HBUFFEROBJECT_ELEM_INT8: - duk_push_int(ctx, (duk_int_t) (duk_int8_t) du.uc[0]); + case DUK_HBUFOBJ_ELEM_INT8: + duk_push_int(thr, (duk_int_t) (duk_int8_t) du.uc[0]); break; - case DUK_HBUFFEROBJECT_ELEM_UINT16: - duk_push_uint(ctx, (duk_uint_t) du.us[0]); + case DUK_HBUFOBJ_ELEM_UINT16: + duk_push_uint(thr, (duk_uint_t) du.us[0]); break; - case DUK_HBUFFEROBJECT_ELEM_INT16: - duk_push_int(ctx, (duk_int_t) (duk_int16_t) du.us[0]); + case DUK_HBUFOBJ_ELEM_INT16: + duk_push_int(thr, (duk_int_t) (duk_int16_t) du.us[0]); break; - case DUK_HBUFFEROBJECT_ELEM_UINT32: - duk_push_uint(ctx, (duk_uint_t) du.ui[0]); + case DUK_HBUFOBJ_ELEM_UINT32: + duk_push_uint(thr, (duk_uint_t) du.ui[0]); break; - case DUK_HBUFFEROBJECT_ELEM_INT32: - duk_push_int(ctx, (duk_int_t) (duk_int32_t) du.ui[0]); + case DUK_HBUFOBJ_ELEM_INT32: + duk_push_int(thr, (duk_int_t) (duk_int32_t) du.ui[0]); break; - case DUK_HBUFFEROBJECT_ELEM_FLOAT32: - duk_push_number(ctx, (duk_double_t) du.f[0]); + case DUK_HBUFOBJ_ELEM_FLOAT32: + duk_push_number(thr, (duk_double_t) du.f[0]); break; - case DUK_HBUFFEROBJECT_ELEM_FLOAT64: - duk_push_number(ctx, (duk_double_t) du.d); + case DUK_HBUFOBJ_ELEM_FLOAT64: + duk_push_number(thr, (duk_double_t) du.d); break; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ default: DUK_UNREACHABLE(); } } -DUK_INTERNAL void duk_hbufferobject_validated_write(duk_context *ctx, duk_hbufferobject *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { +/* Indexed write helper for buffer objects, also called from outside this file. */ +DUK_INTERNAL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) { duk_double_union du; /* NOTE! Caller must ensure that any side effects from the @@ -21972,224 +27552,152 @@ DUK_INTERNAL void duk_hbufferobject_validated_write(duk_context *ctx, duk_hbuffe */ switch (h_bufobj->elem_type) { - case DUK_HBUFFEROBJECT_ELEM_UINT8: - du.uc[0] = (duk_uint8_t) duk_to_uint32(ctx, -1); + case DUK_HBUFOBJ_ELEM_UINT8: + du.uc[0] = (duk_uint8_t) duk_to_uint32(thr, -1); break; -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - /* These are not needed when only Duktape.Buffer is supported. */ - case DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED: - du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(ctx, -1); + case DUK_HBUFOBJ_ELEM_UINT8CLAMPED: + du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_INT8: - du.uc[0] = (duk_uint8_t) duk_to_int32(ctx, -1); + case DUK_HBUFOBJ_ELEM_INT8: + du.uc[0] = (duk_uint8_t) duk_to_int32(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_UINT16: - du.us[0] = (duk_uint16_t) duk_to_uint32(ctx, -1); + case DUK_HBUFOBJ_ELEM_UINT16: + du.us[0] = (duk_uint16_t) duk_to_uint32(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_INT16: - du.us[0] = (duk_uint16_t) duk_to_int32(ctx, -1); + case DUK_HBUFOBJ_ELEM_INT16: + du.us[0] = (duk_uint16_t) duk_to_int32(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_UINT32: - du.ui[0] = (duk_uint32_t) duk_to_uint32(ctx, -1); + case DUK_HBUFOBJ_ELEM_UINT32: + du.ui[0] = (duk_uint32_t) duk_to_uint32(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_INT32: - du.ui[0] = (duk_uint32_t) duk_to_int32(ctx, -1); + case DUK_HBUFOBJ_ELEM_INT32: + du.ui[0] = (duk_uint32_t) duk_to_int32(thr, -1); break; - case DUK_HBUFFEROBJECT_ELEM_FLOAT32: - du.f[0] = (duk_float_t) duk_to_number(ctx, -1); + case DUK_HBUFOBJ_ELEM_FLOAT32: + /* A double-to-float cast is undefined behavior in C99 if + * the cast is out-of-range, so use a helper. Example: + * runtime error: value -1e+100 is outside the range of representable values of type 'float' + */ + du.f[0] = duk_double_to_float_t(duk_to_number_m1(thr)); break; - case DUK_HBUFFEROBJECT_ELEM_FLOAT64: - du.d = (duk_double_t) duk_to_number(ctx, -1); + case DUK_HBUFOBJ_ELEM_FLOAT64: + du.d = (duk_double_t) duk_to_number_m1(thr); break; -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ default: DUK_UNREACHABLE(); } - DUK_MEMCPY((void *) p, (const void *) du.uc, (size_t) elem_size); + DUK_ASSERT(elem_size > 0); + duk_memcpy((void *) p, (const void *) du.uc, (size_t) elem_size); } -/* - * Duktape.Buffer: constructor +/* Helper to create a fixed buffer from argument value at index 0. + * Node.js and allocPlain() compatible. */ - -DUK_INTERNAL duk_ret_t duk_bi_buffer_constructor(duk_context *ctx) { - duk_hthread *thr; - duk_size_t buf_size; - duk_small_int_t buf_dynamic; - duk_uint8_t *buf_data; - const duk_uint8_t *src_data; - - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - /* - * Constructor arguments are currently somewhat compatible with - * (keep it that way if possible): - * - * http://nodejs.org/api/buffer.html - * - * Note that the ToBuffer() coercion (duk_to_buffer()) does NOT match - * the constructor behavior. - */ - - buf_dynamic = duk_get_boolean(ctx, 1); /* default to false */ - - switch (duk_get_type(ctx, 0)) { - case DUK_TYPE_NUMBER: { - /* new buffer of specified size */ - buf_size = (duk_size_t) duk_to_int(ctx, 0); - (void) duk_push_buffer(ctx, buf_size, buf_dynamic); - break; - } - case DUK_TYPE_BUFFER: { - /* return input buffer, converted to a Duktape.Buffer object - * if called as a constructor (no change if called as a - * function). - */ - duk_set_top(ctx, 1); - break; - } - case DUK_TYPE_STRING: { - /* new buffer with string contents */ - src_data = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &buf_size); - DUK_ASSERT(src_data != NULL); /* even for zero-length string */ - buf_data = (duk_uint8_t *) duk_push_buffer(ctx, buf_size, buf_dynamic); - DUK_MEMCPY((void *) buf_data, (const void *) src_data, (size_t) buf_size); - break; - } - case DUK_TYPE_OBJECT: { - /* For all duk_hbufferobjects, get the plain buffer inside - * without making a copy. This is compatible with Duktape 1.2 - * but means that a slice/view information is ignored and the - * full underlying buffer is returned. - * - * If called as a constructor, a new Duktape.Buffer object - * pointing to the same plain buffer is created below. - */ - duk_hbufferobject *h_bufobj; - h_bufobj = (duk_hbufferobject *) duk_get_hobject(ctx, 0); - DUK_ASSERT(h_bufobj != NULL); - if (!DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj)) { - return DUK_RET_TYPE_ERROR; - } - if (h_bufobj->buf == NULL) { - return DUK_RET_TYPE_ERROR; - } - duk_push_hbuffer(ctx, h_bufobj->buf); - break; - } - case DUK_TYPE_NONE: - default: { - return DUK_RET_TYPE_ERROR; - } - } - DUK_ASSERT(duk_is_buffer(ctx, -1)); - - /* stack is unbalanced, but: [ buf ] */ - - if (duk_is_constructor_call(ctx)) { - duk_hbufferobject *h_bufobj; - duk_hbuffer *h_val; - - h_val = duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_val != NULL); - - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), - DUK_BIDX_BUFFER_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); - - duk__set_bufobj_buffer(ctx, h_bufobj, h_val); - - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - } - /* Note: unbalanced stack on purpose */ - - return 1; -} - -/* - * Node.js Buffer: constructor - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) { - /* Internal class is Object: Object.prototype.toString.call(new Buffer(0)) - * prints "[object Object]". - */ +DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_hthread *thr) { duk_int_t len; duk_int_t i; - duk_hbuffer *h_buf; - duk_hbufferobject *h_bufobj; duk_size_t buf_size; + duk_uint8_t *buf; - switch (duk_get_type(ctx, 0)) { - case DUK_TYPE_BUFFER: { - /* Custom behavior: plain buffer is used as internal buffer - * without making a copy (matches Duktape.Buffer). - */ - duk_set_top(ctx, 1); /* -> [ buffer ] */ + switch (duk_get_type(thr, 0)) { + case DUK_TYPE_NUMBER: { + len = duk_to_int_clamped(thr, 0, 0, DUK_INT_MAX); + (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len); break; } - case DUK_TYPE_NUMBER: { - len = duk_to_int_clamped(ctx, 0, 0, DUK_INT_MAX); - (void) duk_push_fixed_buffer(ctx, (duk_size_t) len); - break; + case DUK_TYPE_BUFFER: { /* Treat like Uint8Array. */ + goto slow_copy; } case DUK_TYPE_OBJECT: { - duk_uint8_t *buf; + duk_hobject *h; + duk_hbufobj *h_bufobj; - (void) duk_get_prop_string(ctx, 0, "length"); - len = duk_to_int_clamped(ctx, -1, 0, DUK_INT_MAX); - duk_pop(ctx); - buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len); - for (i = 0; i < len; i++) { - /* XXX: fast path for array arguments? */ - duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); - buf[i] = (duk_uint8_t) (duk_to_uint32(ctx, -1) & 0xffU); - duk_pop(ctx); + /* For Node.js Buffers "Passing an ArrayBuffer returns a Buffer + * that shares allocated memory with the given ArrayBuffer." + * https://nodejs.org/api/buffer.html#buffer_buffer_from_buffer_alloc_and_buffer_allocunsafe + */ + + h = duk_known_hobject(thr, 0); + if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER) { + DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(h)); + h_bufobj = (duk_hbufobj *) h; + if (DUK_UNLIKELY(h_bufobj->buf == NULL)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return NULL;); + } + if (DUK_UNLIKELY(h_bufobj->offset != 0 || h_bufobj->length != DUK_HBUFFER_GET_SIZE(h_bufobj->buf))) { + /* No support for ArrayBuffers with slice + * offset/length. + */ + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return NULL;); + } + duk_push_hbuffer(thr, h_bufobj->buf); + return h_bufobj->buf; } - break; + goto slow_copy; } case DUK_TYPE_STRING: { /* ignore encoding for now */ - duk_dup(ctx, 0); - (void) duk_to_buffer(ctx, -1, &buf_size); + duk_require_hstring_notsymbol(thr, 0); + duk_dup_0(thr); + (void) duk_to_buffer(thr, -1, &buf_size); break; } default: - return DUK_RET_TYPE_ERROR; + DUK_ERROR_TYPE_INVALID_ARGS(thr); + DUK_WO_NORETURN(return NULL;); } - DUK_ASSERT(duk_is_buffer(ctx, -1)); - h_buf = duk_get_hbuffer(ctx, -1); + done: + DUK_ASSERT(duk_is_buffer(thr, -1)); + return duk_known_hbuffer(thr, -1); + + slow_copy: + /* XXX: fast path for typed arrays and other buffer objects? */ + + (void) duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); + len = duk_to_int_clamped(thr, -1, 0, DUK_INT_MAX); + duk_pop(thr); + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len); /* no zeroing, all indices get initialized */ + for (i = 0; i < len; i++) { + /* XXX: fast path for array or buffer arguments? */ + duk_get_prop_index(thr, 0, (duk_uarridx_t) i); + buf[i] = (duk_uint8_t) (duk_to_uint32(thr, -1) & 0xffU); + duk_pop(thr); + } + goto done; +} +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +/* + * Node.js Buffer constructor + * + * Node.js Buffers are just Uint8Arrays with internal prototype set to + * Buffer.prototype so they're handled otherwise the same as Uint8Array. + * However, the constructor arguments are very different so a separate + * constructor entry point is used. + */ +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_hthread *thr) { + duk_hbuffer *h_buf; + + h_buf = duk__hbufobj_fixed_from_argvalue(thr); DUK_ASSERT(h_buf != NULL); - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), - DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); - DUK_ASSERT(h_bufobj != NULL); + duk_push_buffer_object(thr, + -1, + 0, + DUK_HBUFFER_FIXED_GET_SIZE((duk_hbuffer_fixed *) (void *) h_buf), + DUK_BUFOBJ_UINT8ARRAY); + duk_push_hobject_bidx(thr, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); + duk_set_prototype(thr, -2); - h_bufobj->buf = h_buf; - DUK_HBUFFER_INCREF(thr, h_buf); - DUK_ASSERT(h_bufobj->offset == 0); - h_bufobj->length = (duk_int_t) DUK_HBUFFER_GET_SIZE(h_buf); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8); - - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + /* XXX: a more direct implementation */ return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -22197,84 +27705,53 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_bufobj; +DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_hthread *thr) { + duk_hbufobj *h_bufobj; duk_hbuffer *h_val; + duk_int_t len; - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + DUK_CTX_ASSERT_VALID(thr); - /* XXX: function flag to make this automatic? */ - if (!duk_is_constructor_call(ctx)) { - return DUK_RET_TYPE_ERROR; + duk_require_constructor_call(thr); + + len = duk_to_int(thr, 0); + if (len < 0) { + goto fail_length; } + (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len); + h_val = (duk_hbuffer *) duk_known_hbuffer(thr, -1); - if (duk_is_buffer(ctx, 0)) { - /* Custom behavior: plain buffer is used as internal buffer - * without making a copy (matches Duktape.Buffer). - */ - - h_val = duk_get_hbuffer(ctx, 0); - DUK_ASSERT(h_val != NULL); - - /* XXX: accept any duk_hbufferobject type as an input also? */ - } else { - duk_int_t len; - len = duk_to_int(ctx, 0); - if (len < 0) { - goto fail_length; - } - (void) duk_push_fixed_buffer(ctx, (duk_size_t) len); - h_val = (duk_hbuffer *) duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_val != NULL); - -#if !defined(DUK_USE_ZERO_BUFFER_DATA) - /* Khronos/ES6 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA - * is not set. - */ - DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) h_val)); - DUK_MEMZERO((void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_val), (duk_size_t) len); -#endif - } - - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), + DUK_BIDX_ARRAYBUFFER_PROTOTYPE); DUK_ASSERT(h_bufobj != NULL); - duk__set_bufobj_buffer(ctx, h_bufobj, h_val); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + duk__set_bufobj_buffer(thr, h_bufobj, h_val); + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); return 1; fail_length: - return DUK_RET_RANGE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + DUK_DCERROR_RANGE_INVALID_LENGTH(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* Format of magic, bits: * 0...1: elem size shift (0-3) - * 2...5: elem type (DUK_HBUFFEROBJECT_ELEM_xxx) + * 2...5: elem type (DUK_HBUFOBJ_ELEM_xxx) + * + * XXX: add prototype bidx explicitly to magic instead of using a mapping? */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { - duk_hthread *thr; +DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) { duk_tval *tv; duk_hobject *h_obj; - duk_hbufferobject *h_bufobj = NULL; - duk_hbufferobject *h_bufarr = NULL; - duk_hbufferobject *h_bufarg = NULL; + duk_hbufobj *h_bufobj = NULL; + duk_hbufobj *h_bufarg = NULL; duk_hbuffer *h_val; duk_small_uint_t magic; duk_small_uint_t shift; @@ -22288,23 +27765,21 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { duk_uint_t byte_length; duk_small_uint_t copy_mode; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + /* XXX: The same copy helpers could be shared with at least some + * buffer functions. + */ - /* XXX: function flag to make this automatic? */ - if (!duk_is_constructor_call(ctx)) { - return DUK_RET_TYPE_ERROR; - } + duk_require_constructor_call(thr); /* We could fit built-in index into magic but that'd make the magic * number dependent on built-in numbering (genbuiltins.py doesn't * handle that yet). So map both class and prototype from the * element type. */ - magic = duk_get_current_magic(ctx); - shift = magic & 0x03; /* bits 0...1: shift */ - elem_type = (magic >> 2) & 0x0f; /* bits 2...5: type */ - elem_size = 1 << shift; + magic = (duk_small_uint_t) duk_get_current_magic(thr); + shift = magic & 0x03U; /* bits 0...1: shift */ + elem_type = (magic >> 2) & 0x0fU; /* bits 2...5: type */ + elem_size = 1U << shift; align_mask = elem_size - 1; DUK_ASSERT(elem_type < sizeof(duk__buffer_proto_from_elemtype) / sizeof(duk_uint8_t)); proto_bidx = duk__buffer_proto_from_elemtype[elem_type]; @@ -22322,7 +27797,13 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * created. */ - tv = duk_get_tval(ctx, 0); + /* XXX: initial iteration to treat a plain buffer like an ArrayBuffer: + * coerce to an ArrayBuffer object and use that as .buffer. The underlying + * buffer will be the same but result .buffer !== inputPlainBuffer. + */ + duk_hbufobj_promote_plain(thr, 0); + + tv = duk_get_tval(thr, 0); DUK_ASSERT(tv != NULL); /* arg count */ if (DUK_TVAL_IS_OBJECT(tv)) { h_obj = DUK_TVAL_GET_OBJECT(tv); @@ -22336,9 +27817,9 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { duk_int_t byte_offset_signed; duk_uint_t byte_offset; - h_bufarg = (duk_hbufferobject *) h_obj; + h_bufarg = (duk_hbufobj *) h_obj; - byte_offset_signed = duk_to_int(ctx, 1); + byte_offset_signed = duk_to_int(thr, 1); if (byte_offset_signed < 0) { goto fail_arguments; } @@ -22348,7 +27829,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* Must be >= 0 and multiple of element size. */ goto fail_arguments; } - if (duk_is_undefined(ctx, 2)) { + if (duk_is_undefined(thr, 2)) { DUK_ASSERT(h_bufarg->length >= byte_offset); byte_length = h_bufarg->length - byte_offset; if ((byte_length & align_mask) != 0) { @@ -22359,7 +27840,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { } elem_length = (byte_length >> shift); } else { - elem_length_signed = duk_to_int(ctx, 2); + elem_length_signed = duk_to_int(thr, 2); if (elem_length_signed < 0) { goto fail_arguments; } @@ -22383,14 +27864,14 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length); DUK_ASSERT((elem_length << shift) == byte_length); - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(class_num), - proto_bidx); + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(class_num), + (duk_small_int_t) proto_bidx); h_val = h_bufarg->buf; if (h_val == NULL) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); @@ -22398,25 +27879,26 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { h_bufobj->length = byte_length; h_bufobj->shift = (duk_uint8_t) shift; h_bufobj->elem_type = (duk_uint8_t) elem_type; - h_bufobj->is_view = 1; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + h_bufobj->is_typedarray = 1; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); /* Set .buffer to the argument ArrayBuffer. */ - duk_dup(ctx, 0); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE); - duk_compact(ctx, -1); + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = (duk_hobject *) h_bufarg; + DUK_ASSERT(h_bufarg != NULL); + DUK_HBUFOBJ_INCREF(thr, h_bufarg); return 1; - } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { - /* TypedArray (or other non-ArrayBuffer duk_hbufferobject). + } else if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { + /* TypedArray (or other non-ArrayBuffer duk_hbufobj). * Conceptually same behavior as for an Array-like argument, * with a few fast paths. */ - h_bufarg = (duk_hbufferobject *) h_obj; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg); + h_bufarg = (duk_hbufobj *) h_obj; + DUK_HBUFOBJ_ASSERT_VALID(h_bufarg); elem_length_signed = (duk_int_t) (h_bufarg->length >> h_bufarg->shift); if (h_bufarg->buf == NULL) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } /* Select copy mode. Must take into account element @@ -22433,8 +27915,12 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { (long) (elem_length_signed << shift))); copy_mode = 2; /* default is explicit index read/write copy */ +#if !defined(DUK_USE_PREFER_SIZE) + /* With a size optimized build copy_mode 2 is enough. + * Modes 0 and 1 are faster but conceptually the same. + */ DUK_ASSERT(elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t)); - if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)) { + if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) { if ((duk__buffer_elemtype_copy_compatible[elem_type] & (1 << h_bufarg->elem_type)) != 0) { DUK_DDD(DUK_DDDPRINT("source/target are copy compatible, memcpy")); DUK_ASSERT(shift == h_bufarg->shift); /* byte sizes will match */ @@ -22444,25 +27930,18 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { copy_mode = 1; } } +#endif /* !DUK_USE_PREFER_SIZE */ } else { /* Array or Array-like */ - elem_length_signed = (duk_int_t) duk_get_length(ctx, 0); + elem_length_signed = (duk_int_t) duk_get_length(thr, 0); copy_mode = 2; } - } else if (DUK_TVAL_IS_BUFFER(tv)) { - /* Accept plain buffer values like array initializers - * (new in Duktape 1.4.0). - */ - duk_hbuffer *h_srcbuf; - h_srcbuf = DUK_TVAL_GET_BUFFER(tv); - elem_length_signed = (duk_int_t) DUK_HBUFFER_GET_SIZE(h_srcbuf); - copy_mode = 2; /* XXX: could add fast path for u8 compatible views */ } else { /* Non-object argument is simply int coerced, matches * V8 behavior (except for "null", which we coerce to * 0 but V8 TypeErrors). */ - elem_length_signed = duk_to_int(ctx, 0); + elem_length_signed = duk_to_int(thr, 0); copy_mode = 3; } if (elem_length_signed < 0) { @@ -22481,20 +27960,22 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* ArrayBuffer argument is handled specially above; the rest of the * argument variants are handled by shared code below. + * + * ArrayBuffer in h_bufobj->buf_prop is intentionally left unset. + * It will be automatically created by the .buffer accessor on + * first access. */ - /* Push a new ArrayBuffer (becomes view .buffer) */ - h_bufarr = duk__push_arraybuffer_with_length(ctx, byte_length); - DUK_ASSERT(h_bufarr != NULL); - h_val = h_bufarr->buf; + /* Push the resulting view object on top of a plain fixed buffer. */ + (void) duk_push_fixed_buffer(thr, byte_length); + h_val = duk_known_hbuffer(thr, -1); DUK_ASSERT(h_val != NULL); - /* Push the resulting view object and attach the ArrayBuffer. */ - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(class_num), - proto_bidx); + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(class_num), + (duk_small_int_t) proto_bidx); h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); @@ -22502,13 +27983,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { h_bufobj->length = byte_length; h_bufobj->shift = (duk_uint8_t) shift; h_bufobj->elem_type = (duk_uint8_t) elem_type; - h_bufobj->is_view = 1; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - - /* Set .buffer */ - duk_dup(ctx, -2); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE); - duk_compact(ctx, -1); + h_bufobj->is_typedarray = 1; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); /* Copy values, the copy method depends on the arguments. * @@ -22518,6 +27994,10 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { */ DUK_DDD(DUK_DDDPRINT("copy mode: %d", (int) copy_mode)); switch (copy_mode) { + /* Copy modes 0 and 1 can be omitted in size optimized build, + * copy mode 2 handles them (but more slowly). + */ +#if !defined(DUK_USE_PREFER_SIZE) case 0: { /* Use byte copy. */ @@ -22526,18 +28006,18 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_ASSERT(h_bufobj != NULL); DUK_ASSERT(h_bufobj->buf != NULL); - DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)); + DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj)); DUK_ASSERT(h_bufarg != NULL); DUK_ASSERT(h_bufarg->buf != NULL); - DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)); + DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg)); - p_dst = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj); - p_src = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg); + p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj); + p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); DUK_DDD(DUK_DDDPRINT("using memcpy: p_src=%p, p_dst=%p, byte_length=%ld", (void *) p_src, (void *) p_dst, (long) byte_length)); - DUK_MEMCPY((void *) p_dst, (const void *) p_src, (size_t) byte_length); + duk_memcpy_unsafe((void *) p_dst, (const void *) p_src, (size_t) byte_length); break; } case 1: { @@ -22551,16 +28031,16 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_ASSERT(h_bufobj != NULL); DUK_ASSERT(h_bufobj->buf != NULL); - DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)); + DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufobj)); DUK_ASSERT(h_bufarg != NULL); DUK_ASSERT(h_bufarg->buf != NULL); - DUK_ASSERT(DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)); + DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg)); - src_elem_size = 1 << h_bufarg->shift; + src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift); dst_elem_size = elem_size; - p_src = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg); - p_dst = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj); + p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); + p_dst = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj); p_src_end = p_src + h_bufarg->length; DUK_DDD(DUK_DDDPRINT("using fast copy: p_src=%p, p_src_end=%p, p_dst=%p, " @@ -22575,14 +28055,15 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* A validated read() is always a number, so it's write coercion * is always side effect free an won't invalidate pointers etc. */ - duk_hbufferobject_push_validated_read(ctx, h_bufarg, p_src, src_elem_size); - duk_hbufferobject_validated_write(ctx, h_bufobj, p_dst, dst_elem_size); - duk_pop(ctx); + duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size); + duk_hbufobj_validated_write(thr, h_bufobj, p_dst, dst_elem_size); + duk_pop(thr); p_src += src_elem_size; p_dst += dst_elem_size; } break; } +#endif /* !DUK_USE_PREFER_SIZE */ case 2: { /* Copy values by index reads and writes. Let virtual * property handling take care of coercion. @@ -22592,8 +28073,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { DUK_DDD(DUK_DDDPRINT("using slow copy")); for (i = 0; i < elem_length; i++) { - duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); - duk_put_prop_index(ctx, -2, (duk_uarridx_t) i); + duk_get_prop_index(thr, 0, (duk_uarridx_t) i); + duk_put_prop_index(thr, -2, (duk_uarridx_t) i); } break; } @@ -22603,13 +28084,6 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * ambiguity with Float32/Float64 because zero bytes also * represent 0.0. */ -#if !defined(DUK_USE_ZERO_BUFFER_DATA) - /* Khronos/ES6 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA - * is not set. - */ - DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) h_val)); - DUK_MEMZERO((void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_val), (duk_size_t) byte_length); -#endif DUK_DDD(DUK_DDDPRINT("using no copy")); break; @@ -22619,76 +28093,84 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { return 1; fail_arguments: - return DUK_RET_RANGE_ERROR; + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } #else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; +/* When bufferobject support is disabled, new Uint8Array() could still be + * supported to create a plain fixed buffer. Disabled for now. + */ +#if 0 +DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) { + duk_int_t elem_length_signed; + duk_uint_t byte_length; + + /* XXX: The same copy helpers could be shared with at least some + * buffer functions. + */ + + duk_require_constructor_call(thr); + + elem_length_signed = duk_require_int(thr, 0); + if (elem_length_signed < 0) { + goto fail_arguments; + } + byte_length = (duk_uint_t) elem_length_signed; + + (void) duk_push_fixed_buffer_zero(thr, (duk_size_t) byte_length); + return 1; + + fail_arguments: + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } +#endif /* 0 */ #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { - duk_hbufferobject *h_bufarg; - duk_hbufferobject *h_bufobj; +DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_hthread *thr) { + duk_hbufobj *h_bufarg; + duk_hbufobj *h_bufobj; duk_hbuffer *h_val; duk_uint_t offset; duk_uint_t length; - /* XXX: function flag to make this automatic? */ - if (!duk_is_constructor_call(ctx)) { - return DUK_RET_TYPE_ERROR; + duk_require_constructor_call(thr); + + h_bufarg = duk__require_bufobj_value(thr, 0); + DUK_ASSERT(h_bufarg != NULL); + if (DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufarg) != DUK_HOBJECT_CLASS_ARRAYBUFFER) { + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } - h_bufarg = duk__require_bufobj_value(ctx, 0); - DUK_ASSERT(h_bufarg != NULL); - - duk__resolve_offset_opt_length(ctx, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/); + duk__resolve_offset_opt_length(thr, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/); DUK_ASSERT(offset <= h_bufarg->length); DUK_ASSERT(offset + length <= h_bufarg->length); - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW), - DUK_BIDX_DATAVIEW_PROTOTYPE); + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW), + DUK_BIDX_DATAVIEW_PROTOTYPE); h_val = h_bufarg->buf; if (h_val == NULL) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); h_bufobj->offset = h_bufarg->offset + offset; h_bufobj->length = length; DUK_ASSERT(h_bufobj->shift == 0); - DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8); - h_bufobj->is_view = 1; + DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFOBJ_ELEM_UINT8); + DUK_ASSERT(h_bufobj->is_typedarray == 0); - /* The DataView .buffer property is ordinarily set to the argument - * which is an ArrayBuffer. We accept any duk_hbufferobject as - * an argument and .buffer will be set to the argument regardless - * of what it is. This may be a bit confusing if the argument - * is e.g. a DataView or another TypedArray view. - * - * XXX: Copy .buffer property from a DataView/TypedArray argument? - * Create a fresh ArrayBuffer for Duktape.Buffer and Node.js Buffer - * arguments? See: test-bug-dataview-buffer-prop.js. - */ + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = (duk_hobject *) h_bufarg; + DUK_ASSERT(h_bufarg != NULL); + DUK_HBUFOBJ_INCREF(thr, h_bufarg); - duk_dup(ctx, 0); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE); - duk_compact(ctx, -1); - - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -22696,21 +28178,64 @@ DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_hthread *thr) { duk_hobject *h_obj; duk_bool_t ret = 0; - h_obj = duk_get_hobject(ctx, 0); - if (h_obj != NULL && DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { - ret = ((duk_hbufferobject *) h_obj)->is_view; + if (duk_is_buffer(thr, 0)) { + ret = 1; + } else { + h_obj = duk_get_hobject(thr, 0); + if (h_obj != NULL && DUK_HOBJECT_IS_BUFOBJ(h_obj)) { + /* DataView needs special casing: ArrayBuffer.isView() is + * true, but ->is_typedarray is 0. + */ + ret = ((duk_hbufobj *) h_obj)->is_typedarray || + (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_DATAVIEW); + } } - duk_push_boolean(ctx, ret); + duk_push_boolean(thr, ret); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +/* + * Uint8Array.allocPlain() + */ + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_hthread *thr) { + duk__hbufobj_fixed_from_argvalue(thr); + return 1; +} +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +/* + * Uint8Array.plainOf() + */ + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_hthread *thr) { + duk_hbufobj *h_bufobj; + +#if !defined(DUK_USE_PREFER_SIZE) + /* Avoid churn if argument is already a plain buffer. */ + if (duk_is_buffer(thr, 0)) { + return 1; + } +#endif + + /* Promotes plain buffers to ArrayBuffers, so for a plain buffer + * argument we'll create a pointless temporary (but still work + * correctly). + */ + h_bufobj = duk__require_bufobj_value(thr, 0); + if (h_bufobj->buf == NULL) { + duk_push_undefined(thr); + } else { + duk_push_hbuffer(thr, h_bufobj->buf); + } + return 1; } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ @@ -22719,116 +28244,57 @@ DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_hthread *thr) { + duk_hbufobj *h_this; duk_int_t start_offset, end_offset; duk_uint8_t *buf_slice; duk_size_t slice_length; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_this = duk__get_bufobj_this(ctx); + h_this = duk__get_bufobj_this(thr); if (h_this == NULL) { /* XXX: happens e.g. when evaluating: String(Buffer.prototype). */ - duk_push_string(ctx, "[object Object]"); + duk_push_literal(thr, "[object Object]"); return 1; } - DUK_ASSERT_HBUFFEROBJECT_VALID(h_this); + DUK_HBUFOBJ_ASSERT_VALID(h_this); - /* ignore encoding for now */ + /* Ignore encoding for now. */ - duk__clamp_startend_nonegidx_noshift(ctx, h_this, 1 /*idx_start*/, 2 /*idx_end*/, &start_offset, &end_offset); + duk__clamp_startend_nonegidx_noshift(thr, + (duk_int_t) h_this->length, + 1 /*idx_start*/, + 2 /*idx_end*/, + &start_offset, + &end_offset); slice_length = (duk_size_t) (end_offset - start_offset); - buf_slice = (duk_uint8_t *) duk_push_fixed_buffer(ctx, slice_length); + buf_slice = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, slice_length); /* all bytes initialized below */ DUK_ASSERT(buf_slice != NULL); - if (h_this->buf == NULL) { - goto type_error; + /* Neutered or uncovered, TypeError. */ + if (h_this->buf == NULL || + !DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)) { + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } - if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, start_offset + slice_length)) { - DUK_MEMCPY((void *) buf_slice, - (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - (size_t) slice_length); - } else { - /* not covered, return all zeroes */ - ; - } + /* XXX: ideally we wouldn't make a copy but a view into the buffer for the + * decoding process. Or the decoding helper could be changed to accept + * the slice info (a buffer pointer is NOT a good approach because guaranteeing + * its stability is difficult). + */ - duk_to_string(ctx, -1); - return 1; + DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)); + duk_memcpy_unsafe((void *) buf_slice, + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), + (size_t) slice_length); - type_error: - return DUK_RET_TYPE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} -#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ - -/* - * Duktape.Buffer: toString(), valueOf() - */ - -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx) { - duk_hthread *thr; - duk_tval *tv; - duk_small_int_t to_string = duk_get_current_magic(ctx); - - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - tv = duk_get_borrowed_this_tval(ctx); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_BUFFER(tv)) { - duk_hbuffer *h_buf; - h_buf = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h_buf != NULL); - duk_push_hbuffer(ctx, h_buf); - } else if (DUK_TVAL_IS_OBJECT(tv)) { - duk_hobject *h; - duk_hbufferobject *h_bufobj; - - /* Accept any duk_hbufferobject, though we're only normally - * called for Duktape.Buffer values. - */ - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_IS_BUFFEROBJECT(h)) { - DUK_DD(DUK_DDPRINT("toString/valueOf() called for a non-bufferobject object")); - goto type_error; - } - h_bufobj = (duk_hbufferobject *) h; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - - if (h_bufobj->buf == NULL) { - DUK_DD(DUK_DDPRINT("toString/valueOf() called for a bufferobject with NULL buf")); - goto type_error; - } - duk_push_hbuffer(ctx, h_bufobj->buf); - } else { - goto type_error; - } - - if (to_string) { - duk_to_string(ctx, -1); - } - return 1; - - type_error: - return DUK_RET_TYPE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + /* Use the equivalent of: new TextEncoder().encode(this) to convert the + * string. Result will be valid UTF-8; non-CESU-8 inputs are currently + * interpreted loosely. Value stack convention is a bit odd for now. + */ + duk_replace(thr, 0); + duk_set_top(thr, 1); + return duk_textdecoder_decode_utf8_nodejs(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ @@ -22837,48 +28303,41 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_prototype_tostring_shared(duk_context *ctx) */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_hthread *thr) { + duk_hbufobj *h_this; duk_uint8_t *buf; - duk_uint_t i; + duk_uint_t i, n; + duk_tval *tv; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - h_this = duk__require_bufobj_this(ctx); + h_this = duk__require_bufobj_this(thr); DUK_ASSERT(h_this != NULL); - if (h_this->buf == NULL || !DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) { + if (h_this->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_this)) { /* Serialize uncovered backing buffer as a null; doesn't * really matter as long we're memory safe. */ - duk_push_null(ctx); + duk_push_null(thr); return 1; } - duk_push_object(ctx); - duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_BUFFER); - duk_put_prop_stridx(ctx, -2, DUK_STRIDX_TYPE); + duk_push_object(thr); + duk_push_hstring_stridx(thr, DUK_STRIDX_UC_BUFFER); + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_TYPE); - duk_push_array(ctx); - for (i = 0; i < h_this->length; i++) { - /* XXX: regetting the pointer may be overkill - we're writing - * to a side-effect free array here. - */ - DUK_ASSERT(h_this->buf != NULL); - buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this); - duk_push_uint(ctx, (duk_uint_t) buf[i]); - duk_put_prop_index(ctx, -2, (duk_idx_t) i); + /* XXX: uninitialized would be OK */ + DUK_ASSERT_DISABLE((duk_size_t) h_this->length <= (duk_size_t) DUK_UINT32_MAX); + tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) h_this->length); /* XXX: needs revision with >4G buffers */ + DUK_ASSERT(!duk_is_bare_object(thr, -1)); + + DUK_ASSERT(h_this->buf != NULL); + buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); + for (i = 0, n = h_this->length; i < n; i++) { + DUK_TVAL_SET_U32(tv + i, (duk_uint32_t) buf[i]); /* no need for decref or incref */ } - duk_put_prop_stridx(ctx, -2, DUK_STRIDX_DATA); + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_DATA); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -22888,24 +28347,22 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { - duk_hthread *thr; +DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_hthread *thr) { duk_small_uint_t magic; - duk_hbufferobject *h_bufarg1; - duk_hbufferobject *h_bufarg2; + duk_hbufobj *h_bufarg1; + duk_hbufobj *h_bufarg2; duk_small_int_t comp_res; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + /* XXX: keep support for plain buffers and non-Node.js buffers? */ - magic = duk_get_current_magic(ctx); - if (magic & 0x02) { + magic = (duk_small_uint_t) duk_get_current_magic(thr); + if (magic & 0x02U) { /* Static call style. */ - h_bufarg1 = duk__require_bufobj_value(ctx, 0); - h_bufarg2 = duk__require_bufobj_value(ctx, 1); + h_bufarg1 = duk__require_bufobj_value(thr, 0); + h_bufarg2 = duk__require_bufobj_value(thr, 1); } else { - h_bufarg1 = duk__require_bufobj_this(ctx); - h_bufarg2 = duk__require_bufobj_value(ctx, 0); + h_bufarg1 = duk__require_bufobj_this(thr); + h_bufarg2 = duk__require_bufobj_value(thr, 0); } DUK_ASSERT(h_bufarg1 != NULL); DUK_ASSERT(h_bufarg2 != NULL); @@ -22916,8 +28373,8 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { * matters is to be memory safe. */ - if (DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg1) && - DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg2)) { + if (DUK_HBUFOBJ_VALID_SLICE(h_bufarg1) && + DUK_HBUFOBJ_VALID_SLICE(h_bufarg2)) { comp_res = duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg1->buf) + h_bufarg1->offset, (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufarg2->buf) + h_bufarg2->offset, (duk_size_t) h_bufarg1->length, @@ -22926,21 +28383,16 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { comp_res = -1; /* either nonzero value is ok */ } - if (magic & 0x01) { + if (magic & 0x01U) { /* compare: similar to string comparison but for buffer data. */ - duk_push_int(ctx, comp_res); + duk_push_int(thr, comp_res); } else { /* equals */ - duk_push_boolean(ctx, (comp_res == 0)); + duk_push_boolean(thr, (comp_res == 0)); } return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -22948,9 +28400,8 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_hthread *thr) { + duk_hbufobj *h_this; const duk_uint8_t *fill_str_ptr; duk_size_t fill_str_len; duk_uint8_t fill_value; @@ -22959,29 +28410,32 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { duk_size_t fill_length; duk_uint8_t *p; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_this = duk__require_bufobj_this(ctx); + h_this = duk__require_bufobj_this(thr); DUK_ASSERT(h_this != NULL); if (h_this->buf == NULL) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } /* [ value offset end ] */ - if (duk_is_string(ctx, 0)) { - fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &fill_str_len); + if (duk_is_string_notsymbol(thr, 0)) { + fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(thr, 0, &fill_str_len); DUK_ASSERT(fill_str_ptr != NULL); } else { - fill_value = (duk_uint8_t) duk_to_uint32(ctx, 0); + /* Symbols get ToNumber() coerced and cause TypeError. */ + fill_value = (duk_uint8_t) duk_to_uint32(thr, 0); fill_str_ptr = (const duk_uint8_t *) &fill_value; fill_str_len = 1; } /* Fill offset handling is more lenient than in Node.js. */ - duk__clamp_startend_nonegidx_noshift(ctx, h_this, 1 /*idx_start*/, 2 /*idx_end*/, &fill_offset, &fill_end); + duk__clamp_startend_nonegidx_noshift(thr, + (duk_int_t) h_this->length, + 1 /*idx_start*/, + 2 /*idx_end*/, + &fill_offset, + &fill_end); DUK_DDD(DUK_DDDPRINT("fill: fill_value=%02x, fill_offset=%ld, fill_end=%ld, view length=%ld", (unsigned int) fill_value, (long) fill_offset, (long) fill_end, (long) h_this->length)); @@ -22989,17 +28443,17 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { DUK_ASSERT(fill_end - fill_offset >= 0); DUK_ASSERT(h_this->buf != NULL); - p = (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + fill_offset); + p = (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + fill_offset); fill_length = (duk_size_t) (fill_end - fill_offset); if (fill_str_len == 1) { /* Handle single character fills as memset() even when * the fill data comes from a one-char argument. */ - DUK_MEMSET((void *) p, (int) fill_str_ptr[0], (size_t) fill_length); + duk_memset_unsafe((void *) p, (int) fill_str_ptr[0], (size_t) fill_length); } else if (fill_str_len > 1) { duk_size_t i, n, t; - for (i = 0, n = (fill_end - fill_offset), t = 0; i < n; i++) { + for (i = 0, n = (duk_size_t) (fill_end - fill_offset), t = 0; i < n; i++) { p[i] = fill_str_ptr[t++]; if (t >= fill_str_len) { t = 0; @@ -23010,14 +28464,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { } /* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */ - duk_push_this(ctx); + duk_push_this(thr); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23025,24 +28474,21 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_hthread *thr) { + duk_hbufobj *h_this; duk_uint_t offset; duk_uint_t length; const duk_uint8_t *str_data; duk_size_t str_len; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_this = duk__require_bufobj_this(ctx); + /* XXX: very inefficient support for plain buffers */ + h_this = duk__require_bufobj_this(thr); DUK_ASSERT(h_this != NULL); /* Argument must be a string, e.g. a buffer is not allowed. */ - str_data = (const duk_uint8_t *) duk_require_lstring(ctx, 0, &str_len); + str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(thr, 0, &str_len); - duk__resolve_offset_opt_length(ctx, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/); + duk__resolve_offset_opt_length(thr, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/); DUK_ASSERT(offset <= h_this->length); DUK_ASSERT(offset + length <= h_this->length); @@ -23052,23 +28498,18 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { length = (duk_uint_t) str_len; } - if (DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) { + if (DUK_HBUFOBJ_VALID_SLICE(h_this)) { /* Cannot overlap. */ - DUK_MEMCPY((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + offset), - (const void *) str_data, - (size_t) length); + duk_memcpy_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset), + (const void *) str_data, + (size_t) length); } else { DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore")); } - duk_push_uint(ctx, length); + duk_push_uint(thr, length); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23076,10 +28517,9 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; - duk_hbufferobject *h_bufarg; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) { + duk_hbufobj *h_this; + duk_hbufobj *h_bufarg; duk_int_t source_length; duk_int_t target_length; duk_int_t target_start, source_start, source_end; @@ -23088,22 +28528,19 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { /* [ targetBuffer targetStart sourceStart sourceEnd ] */ - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_this = duk__require_bufobj_this(ctx); - h_bufarg = duk__require_bufobj_value(ctx, 0); + h_this = duk__require_bufobj_this(thr); + h_bufarg = duk__require_bufobj_value(thr, 0); DUK_ASSERT(h_this != NULL); DUK_ASSERT(h_bufarg != NULL); source_length = (duk_int_t) h_this->length; target_length = (duk_int_t) h_bufarg->length; - target_start = duk_to_int(ctx, 1); - source_start = duk_to_int(ctx, 2); - if (duk_is_undefined(ctx, 3)) { + target_start = duk_to_int(thr, 1); + source_start = duk_to_int(thr, 2); + if (duk_is_undefined(thr, 3)) { source_end = source_length; } else { - source_end = duk_to_int(ctx, 3); + source_end = duk_to_int(thr, 3); } DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, " @@ -23127,7 +28564,7 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { } if (source_uend >= (duk_uint_t) source_length) { /* Source end clamped silently to available length. */ - source_uend = source_length; + source_uend = (duk_uint_t) source_length; } copy_size = source_uend - source_ustart; if (target_ustart + copy_size > (duk_uint_t) target_length) { @@ -23156,14 +28593,14 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { /* Ensure copy is covered by underlying buffers. */ DUK_ASSERT(h_bufarg->buf != NULL); /* length check */ DUK_ASSERT(h_this->buf != NULL); /* length check */ - if (DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufarg, target_ustart + copy_size) && - DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, source_ustart + copy_size)) { + if (DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_bufarg, target_ustart + copy_size) && + DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, source_ustart + copy_size)) { /* Must use memmove() because copy area may overlap (source and target * buffer may be the same, or from different slices. */ - DUK_MEMMOVE((void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart), - (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + source_ustart), - (size_t) copy_size); + duk_memmove_unsafe((void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg) + target_ustart), + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + source_ustart), + (size_t) copy_size); } else { DUK_DDD(DUK_DDDPRINT("buffer copy not covered by underlying buffer(s), ignoring")); } @@ -23173,16 +28610,11 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { * The return value matters because of code like: * "off += buf.copy(...)". */ - duk_push_uint(ctx, copy_size); + duk_push_uint(thr, copy_size); return 1; fail_bounds: - return DUK_RET_RANGE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ @@ -23223,59 +28655,58 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { - duk_hthread *thr; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) { + duk_hbufobj *h_this; duk_hobject *h_obj; duk_uarridx_t i, n; duk_int_t offset_signed; duk_uint_t offset_elems; duk_uint_t offset_bytes; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - h_this = duk__require_bufobj_this(ctx); + h_this = duk__require_bufobj_this(thr); DUK_ASSERT(h_this != NULL); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_this); + DUK_HBUFOBJ_ASSERT_VALID(h_this); if (h_this->buf == NULL) { DUK_DDD(DUK_DDDPRINT("source neutered, skip copy")); return 0; } - h_obj = duk_require_hobject(ctx, 0); - DUK_ASSERT(h_obj != NULL); + duk_hbufobj_promote_plain(thr, 0); + h_obj = duk_require_hobject(thr, 0); /* XXX: V8 throws a TypeError for negative values. Would it * be more useful to interpret negative offsets here from the * end of the buffer too? */ - offset_signed = duk_to_int(ctx, 1); + offset_signed = duk_to_int(thr, 1); if (offset_signed < 0) { - return DUK_RET_TYPE_ERROR; + /* For some reason this is a TypeError (at least in V8). */ + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } offset_elems = (duk_uint_t) offset_signed; offset_bytes = offset_elems << h_this->shift; if ((offset_bytes >> h_this->shift) != offset_elems) { /* Byte length would overflow. */ /* XXX: easier check with less code? */ - return DUK_RET_RANGE_ERROR; + goto fail_args; } if (offset_bytes > h_this->length) { /* Equality may be OK but >length not. Checking * this explicitly avoids some overflow cases * below. */ - return DUK_RET_RANGE_ERROR; + goto fail_args; } DUK_ASSERT(offset_bytes <= h_this->length); - /* Fast path: source is a TypedArray (or any bufferobject). */ + /* Fast path: source is a TypedArray (or any bufobj). */ - if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { - duk_hbufferobject *h_bufarg; + if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { + duk_hbufobj *h_bufarg; +#if !defined(DUK_USE_PREFER_SIZE) duk_uint16_t comp_mask; +#endif duk_small_int_t no_overlap = 0; duk_uint_t src_length; duk_uint_t dst_length; @@ -23288,8 +28719,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { duk_small_uint_t src_elem_size; duk_small_uint_t dst_elem_size; - h_bufarg = (duk_hbufferobject *) h_obj; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufarg); + h_bufarg = (duk_hbufobj *) h_obj; + DUK_HBUFOBJ_ASSERT_VALID(h_bufarg); if (h_bufarg->buf == NULL) { DUK_DDD(DUK_DDDPRINT("target neutered, skip copy")); @@ -23303,7 +28734,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { if ((dst_length >> h_this->shift) != dst_length_elems) { /* Byte length would overflow. */ /* XXX: easier check with less code? */ - return DUK_RET_RANGE_ERROR; + goto fail_args; } DUK_DDD(DUK_DDDPRINT("nominal size check: src_length=%ld, dst_length=%ld", (long) src_length, (long) dst_length)); @@ -23313,22 +28744,22 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * side and guaranteed to be >= 0. */ DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length")); - return DUK_RET_RANGE_ERROR; + goto fail_args; } - if (!DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_this, offset_bytes + dst_length)) { + if (!DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, offset_bytes + dst_length)) { DUK_DDD(DUK_DDDPRINT("copy not covered by underlying target buffer, ignore")); return 0; } - p_src_base = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufarg); - p_dst_base = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + offset_bytes; + p_src_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg); + p_dst_base = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + offset_bytes; /* Check actual underlying buffers for validity and that they * cover the copy. No side effects are allowed after the check * so that the validity status doesn't change. */ - if (!DUK_HBUFFEROBJECT_VALID_SLICE(h_this) || - !DUK_HBUFFEROBJECT_VALID_SLICE(h_bufarg)) { + if (!DUK_HBUFOBJ_VALID_SLICE(h_this) || + !DUK_HBUFOBJ_VALID_SLICE(h_bufarg)) { /* The condition could be more narrow and check for the * copy area only, but there's no need for fine grained * behavior when the underlying buffer is misconfigured. @@ -23348,16 +28779,18 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * and destination element sizes are necessarily equal. */ +#if !defined(DUK_USE_PREFER_SIZE) DUK_ASSERT(h_this->elem_type < sizeof(duk__buffer_elemtype_copy_compatible) / sizeof(duk_uint16_t)); comp_mask = duk__buffer_elemtype_copy_compatible[h_this->elem_type]; if (comp_mask & (1 << h_bufarg->elem_type)) { DUK_ASSERT(src_length == dst_length); DUK_DDD(DUK_DDDPRINT("fast path: able to use memmove() because views are compatible")); - DUK_MEMMOVE((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length); + duk_memmove_unsafe((void *) p_dst_base, (const void *) p_src_base, (size_t) dst_length); return 0; } DUK_DDD(DUK_DDDPRINT("fast path: views are not compatible with a byte copy, copy by item")); +#endif /* !DUK_USE_PREFER_SIZE */ /* We want to avoid making a copy to process set() but that's * not always possible: the source and the target may overlap @@ -23394,9 +28827,9 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { duk_uint8_t *p_src_copy; DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source")); - p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer(ctx, src_length); + p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_length); DUK_ASSERT(p_src_copy != NULL); - DUK_MEMCPY((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length); + duk_memcpy_unsafe((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length); p_src_base = p_src_copy; /* use p_src_base from now on */ } @@ -23406,7 +28839,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { "p_dst_base=%p, dst_length=%ld, valstack top=%ld", (void *) p_src_base, (long) src_length, (void *) p_dst_base, (long) dst_length, - (long) duk_get_top(ctx))); + (long) duk_get_top(thr))); /* Ready to make the copy. We must proceed element by element * and must avoid any side effects that might cause the buffer @@ -23416,8 +28849,8 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * numbers are handled which should be side effect safe. */ - src_elem_size = 1 << h_bufarg->shift; - dst_elem_size = 1 << h_this->shift; + src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift); + dst_elem_size = (duk_small_uint_t) (1U << h_this->shift); p_src = p_src_base; p_dst = p_dst_base; p_src_end = p_src_base + src_length; @@ -23429,9 +28862,9 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { /* A validated read() is always a number, so it's write coercion * is always side effect free an won't invalidate pointers etc. */ - duk_hbufferobject_push_validated_read(ctx, h_bufarg, p_src, src_elem_size); - duk_hbufferobject_validated_write(ctx, h_this, p_dst, dst_elem_size); - duk_pop(ctx); + duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size); + duk_hbufobj_validated_write(thr, h_this, p_dst, dst_elem_size); + duk_pop(thr); p_src += src_elem_size; p_dst += dst_elem_size; } @@ -23442,19 +28875,19 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * to write coerce target values. We don't need to worry about overlap * here because the source is not a TypedArray. * - * We could use the bufferobject write coercion helper but since the + * We could use the bufobj write coercion helper but since the * property read may have arbitrary side effects, full validity checks * would be needed for every element anyway. */ - n = (duk_uarridx_t) duk_get_length(ctx, 0); + n = (duk_uarridx_t) duk_get_length(thr, 0); DUK_ASSERT(offset_bytes <= h_this->length); if ((n << h_this->shift) > h_this->length - offset_bytes) { /* Overflow not an issue because subtraction is used on the right * side and guaranteed to be >= 0. */ DUK_DDD(DUK_DDDPRINT("copy exceeds target buffer nominal length")); - return DUK_RET_RANGE_ERROR; + goto fail_args; } /* There's no need to check for buffer validity status for the @@ -23464,28 +28897,26 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * the results anyway. */ - DUK_ASSERT_TOP(ctx, 2); - duk_push_this(ctx); + DUK_ASSERT_TOP(thr, 2); + duk_push_this(thr); for (i = 0; i < n; i++) { - duk_get_prop_index(ctx, 0, i); - duk_put_prop_index(ctx, 2, offset_elems + i); + duk_get_prop_index(thr, 0, i); + duk_put_prop_index(thr, 2, offset_elems + i); } } return 0; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + + fail_args: + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* * Node.js Buffer.prototype.slice([start], [end]) * ArrayBuffer.prototype.slice(begin, [end]) - * TypedArray.prototype.slice(begin, [end]) + * TypedArray.prototype.subarray(begin, [end]) * * The API calls are almost identical; negative indices are counted from end * of buffer, and final indices are clamped (allowing crossed indices). Main @@ -23498,27 +28929,86 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * call (or 'this' argument) * * - TypedArray .subarray() arguments are element indices, not byte offsets + * + * - Plain buffer argument creates a plain buffer slice */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { - duk_hthread *thr; +DUK_LOCAL void duk__arraybuffer_plain_slice(duk_hthread *thr, duk_hbuffer *h_val) { + duk_int_t start_offset, end_offset; + duk_uint_t slice_length; + duk_uint8_t *p_copy; + duk_size_t copy_length; + + duk__clamp_startend_negidx_shifted(thr, + (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val), + 0 /*buffer_shift*/, + 0 /*idx_start*/, + 1 /*idx_end*/, + &start_offset, + &end_offset); + DUK_ASSERT(end_offset <= (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val)); + DUK_ASSERT(start_offset >= 0); + DUK_ASSERT(end_offset >= start_offset); + slice_length = (duk_uint_t) (end_offset - start_offset); + + p_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) slice_length); + DUK_ASSERT(p_copy != NULL); + copy_length = slice_length; + + duk_memcpy_unsafe((void *) p_copy, + (const void *) ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_val) + start_offset), + copy_length); +} +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +/* Shared helper for slice/subarray operation. + * Magic: 0x01=isView, 0x02=copy, 0x04=Node.js Buffer special handling. + */ +DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_hthread *thr) { duk_small_int_t magic; duk_small_uint_t res_class_num; - duk_hobject *res_proto; - duk_hbufferobject *h_this; - duk_hbufferobject *h_bufobj; + duk_small_int_t res_proto_bidx; + duk_hbufobj *h_this; + duk_hbufobj *h_bufobj; duk_hbuffer *h_val; duk_int_t start_offset, end_offset; duk_uint_t slice_length; - - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + duk_tval *tv; /* [ start end ] */ - magic = duk_get_current_magic(ctx); - h_this = duk__require_bufobj_this(ctx); + magic = duk_get_current_magic(thr); + + tv = duk_get_borrowed_this_tval(thr); + DUK_ASSERT(tv != NULL); + + if (DUK_TVAL_IS_BUFFER(tv)) { + /* For plain buffers return a plain buffer slice. */ + h_val = DUK_TVAL_GET_BUFFER(tv); + DUK_ASSERT(h_val != NULL); + + if (magic & 0x02) { + /* Make copy: ArrayBuffer.prototype.slice() uses this. */ + duk__arraybuffer_plain_slice(thr, h_val); + return 1; + } else { + /* View into existing buffer: cannot be done if the + * result is a plain buffer because there's no slice + * info. So return an ArrayBuffer instance; coerce + * the 'this' binding into an object and behave as if + * the original call was for an Object-coerced plain + * buffer (handled automatically by duk__require_bufobj_this()). + */ + + DUK_DDD(DUK_DDDPRINT("slice() doesn't handle view into plain buffer, coerce 'this' to ArrayBuffer object")); + /* fall through */ + } + } + tv = NULL; /* No longer valid nor needed. */ + + h_this = duk__require_bufobj_this(thr); /* Slice offsets are element (not byte) offsets, which only matters * for TypedArray views, Node.js Buffer and ArrayBuffer have shift @@ -23529,40 +29019,53 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { * against the underlying buffer here. */ - duk__clamp_startend_negidx_shifted(ctx, h_this, 0 /*idx_start*/, 1 /*idx_end*/, &start_offset, &end_offset); + duk__clamp_startend_negidx_shifted(thr, + (duk_int_t) h_this->length, + (duk_uint8_t) h_this->shift, + 0 /*idx_start*/, + 1 /*idx_end*/, + &start_offset, + &end_offset); DUK_ASSERT(end_offset >= start_offset); + DUK_ASSERT(start_offset >= 0); + DUK_ASSERT(end_offset >= 0); slice_length = (duk_uint_t) (end_offset - start_offset); /* The resulting buffer object gets the same class and prototype as - * the buffer in 'this', e.g. if the input is a Node.js Buffer the - * result is a Node.js Buffer; if the input is a Float32Array, the - * result is a Float32Array. + * the buffer in 'this', e.g. if the input is a Uint8Array the + * result is a Uint8Array; if the input is a Float32Array, the + * result is a Float32Array. The result internal prototype should + * be the default prototype for the class (e.g. initial value of + * Uint8Array.prototype), not copied from the argument (Duktape 1.x + * did that). * - * For the class number this seems correct. The internal prototype - * is not so clear: if 'this' is a bufferobject with a non-standard - * prototype object, that value gets copied over into the result - * (instead of using the standard prototype for that object type). + * Node.js Buffers have special handling: they're Uint8Arrays as far + * as the internal class is concerned, so the new Buffer should also + * be an Uint8Array but inherit from Buffer.prototype. */ - res_class_num = DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_this); - h_bufobj = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num), - DUK_BIDX_OBJECT_PROTOTYPE); /* replaced */ + DUK_ASSERT(res_class_num >= DUK_HOBJECT_CLASS_BUFOBJ_MIN); /* type check guarantees */ + DUK_ASSERT(res_class_num <= DUK_HOBJECT_CLASS_BUFOBJ_MAX); + res_proto_bidx = duk__buffer_proto_from_classnum[res_class_num - DUK_HOBJECT_CLASS_BUFOBJ_MIN]; + if (magic & 0x04) { + res_proto_bidx = DUK_BIDX_NODEJS_BUFFER_PROTOTYPE; + } + h_bufobj = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num), + res_proto_bidx); DUK_ASSERT(h_bufobj != NULL); - res_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_this); /* may be NULL */ - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_bufobj, res_proto); - h_bufobj->length = slice_length; + DUK_ASSERT(h_bufobj->length == 0); h_bufobj->shift = h_this->shift; /* inherit */ h_bufobj->elem_type = h_this->elem_type; /* inherit */ - h_bufobj->is_view = magic & 0x01; - DUK_ASSERT(h_bufobj->is_view == 0 || h_bufobj->is_view == 1); + h_bufobj->is_typedarray = magic & 0x01; + DUK_ASSERT(h_bufobj->is_typedarray == 0 || h_bufobj->is_typedarray == 1); h_val = h_this->buf; if (h_val == NULL) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } if (magic & 0x02) { @@ -23570,53 +29073,45 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { duk_uint8_t *p_copy; duk_size_t copy_length; - p_copy = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) slice_length); + p_copy = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, (duk_size_t) slice_length); /* must be zeroed, not all bytes always copied */ DUK_ASSERT(p_copy != NULL); /* Copy slice, respecting underlying buffer limits; remainder * is left as zero. */ - copy_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, slice_length); - DUK_MEMCPY((void *) p_copy, - (const void *) (DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this) + start_offset), - copy_length); + copy_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, slice_length); + duk_memcpy_unsafe((void *) p_copy, + (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset), + copy_length); - h_val = duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_val != NULL); + h_val = duk_known_hbuffer(thr, -1); h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); + h_bufobj->length = slice_length; DUK_ASSERT(h_bufobj->offset == 0); - duk_pop(ctx); /* reachable so pop OK */ + duk_pop(thr); /* reachable so pop OK */ } else { h_bufobj->buf = h_val; DUK_HBUFFER_INCREF(thr, h_val); - h_bufobj->offset = (duk_uint_t) (h_this->offset + start_offset); + h_bufobj->length = slice_length; + h_bufobj->offset = h_this->offset + (duk_uint_t) start_offset; /* Copy the .buffer property, needed for TypedArray.prototype.subarray(). * * XXX: limit copy only for TypedArray classes specifically? */ - duk_push_this(ctx); - if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LC_BUFFER)) { - duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE); - duk_pop(ctx); - } else { - duk_pop_2(ctx); - } + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = h_this->buf_prop; /* may be NULL */ + DUK_HOBJECT_INCREF_ALLOWNULL(thr, (duk_hobject *) h_bufobj->buf_prop); } /* unbalanced stack on purpose */ - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23624,21 +29119,16 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_hthread *thr) { const char *encoding; /* only accept lowercase 'utf8' now. */ - encoding = duk_to_string(ctx, 0); - DUK_ASSERT(duk_is_string(ctx, 0)); /* guaranteed by duk_to_string() */ - duk_push_boolean(ctx, DUK_STRCMP(encoding, "utf8") == 0); + encoding = duk_to_string(thr, 0); + DUK_ASSERT(duk_is_string(thr, 0)); /* guaranteed by duk_to_string() */ + duk_push_boolean(thr, DUK_STRCMP(encoding, "utf8") == 0); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23646,40 +29136,26 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) { - duk_hthread *thr; - duk_tval *tv; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_hthread *thr) { duk_hobject *h; duk_hobject *h_proto; duk_bool_t ret = 0; - thr = (duk_hthread *) ctx; - - DUK_ASSERT(duk_get_top(ctx) >= 1); /* nargs */ - tv = duk_get_tval(ctx, 0); - DUK_ASSERT(tv != NULL); - - if (DUK_TVAL_IS_OBJECT(tv)) { - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - + DUK_ASSERT(duk_get_top(thr) >= 1); /* nargs */ + h = duk_get_hobject(thr, 0); + if (h != NULL) { h_proto = thr->builtins[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE]; DUK_ASSERT(h_proto != NULL); h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - if (h) { + if (h != NULL) { ret = duk_hobject_prototype_chain_contains(thr, h, h_proto, 0 /*ignore_loop*/); } } - duk_push_boolean(ctx, ret); + duk_push_boolean(thr, ret); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23687,7 +29163,7 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_hthread *thr) { const char *str; duk_size_t len; @@ -23696,16 +29172,21 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { * unconditionally. */ - str = duk_to_lstring(ctx, 0, &len); + /* XXX: to be revised; Old Node.js behavior just coerces any buffer + * values to string: + * $ node + * > Buffer.byteLength(new Uint32Array(10)) + * 20 + * > Buffer.byteLength(new Uint32Array(100)) + * 20 + * (The 20 comes from '[object Uint32Array]'.length + */ + + str = duk_to_lstring(thr, 0, &len); DUK_UNREF(str); - duk_push_size_t(ctx, len); + duk_push_size_t(thr, len); return 1; } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23713,81 +29194,77 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { - duk_hthread *thr; +DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_hthread *thr) { duk_hobject *h_arg; - duk_int_t total_length = 0; - duk_hbufferobject *h_bufobj; - duk_hbufferobject *h_bufres; + duk_uint_t total_length; + duk_hbufobj *h_bufobj; + duk_hbufobj *h_bufres; duk_hbuffer *h_val; duk_uint_t i, n; duk_uint8_t *p; duk_size_t space_left; duk_size_t copy_size; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - /* Node.js accepts only actual Arrays. */ - h_arg = duk_require_hobject(ctx, 0); + h_arg = duk_require_hobject(thr, 0); if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg) != DUK_HOBJECT_CLASS_ARRAY) { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } /* Compute result length and validate argument buffers. */ - n = (duk_uint_t) duk_get_length(ctx, 0); + n = (duk_uint_t) duk_get_length(thr, 0); + total_length = 0; for (i = 0; i < n; i++) { /* Neutered checks not necessary here: neutered buffers have * zero 'length' so we'll effectively skip them. */ - DUK_ASSERT_TOP(ctx, 2); /* [ array totalLength ] */ - duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); /* -> [ array totalLength buf ] */ - h_bufobj = duk__require_bufobj_value(ctx, 2); + DUK_ASSERT_TOP(thr, 2); /* [ array totalLength ] */ + duk_get_prop_index(thr, 0, (duk_uarridx_t) i); /* -> [ array totalLength buf ] */ + h_bufobj = duk__require_bufobj_value(thr, 2); DUK_ASSERT(h_bufobj != NULL); total_length += h_bufobj->length; - duk_pop(ctx); - } - if (n == 1) { - /* For the case n==1 Node.js doesn't seem to type check - * the sole member but we do it before returning it. - * For this case only the original buffer object is - * returned (not a copy). - */ - duk_get_prop_index(ctx, 0, 0); - return 1; + if (DUK_UNLIKELY(total_length < h_bufobj->length)) { + DUK_DCERROR_RANGE_INVALID_ARGS(thr); /* Wrapped. */ + } + duk_pop(thr); } + /* In Node.js v0.12.1 a 1-element array is special and won't create a + * copy, this was fixed later so an explicit check no longer needed. + */ /* User totalLength overrides a computed length, but we'll check - * every copy in the copy loop. Note that duk_to_uint() can + * every copy in the copy loop. Note that duk_to_int() can * technically have arbitrary side effects so we need to recheck * the buffers in the copy loop. */ - if (!duk_is_undefined(ctx, 1) && n > 0) { + if (!duk_is_undefined(thr, 1) && n > 0) { /* For n == 0, Node.js ignores totalLength argument and * returns a zero length buffer. */ - total_length = duk_to_int(ctx, 1); - } - if (total_length < 0) { - return DUK_RET_RANGE_ERROR; + duk_int_t total_length_signed; + total_length_signed = duk_to_int(thr, 1); + if (total_length_signed < 0) { + DUK_DCERROR_RANGE_INVALID_ARGS(thr); + } + total_length = (duk_uint_t) total_length_signed; } - h_bufres = duk_push_bufferobject_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFFEROBJECT | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER), - DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); + h_bufres = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY), + DUK_BIDX_NODEJS_BUFFER_PROTOTYPE); DUK_ASSERT(h_bufres != NULL); - p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, total_length); + p = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, total_length); /* must be zeroed, all bytes not necessarily written over */ DUK_ASSERT(p != NULL); - space_left = total_length; + space_left = (duk_size_t) total_length; for (i = 0; i < n; i++) { - DUK_ASSERT_TOP(ctx, 4); /* [ array totalLength bufres buf ] */ + DUK_ASSERT_TOP(thr, 4); /* [ array totalLength bufres buf ] */ - duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); - h_bufobj = duk__require_bufobj_value(ctx, 4); + duk_get_prop_index(thr, 0, (duk_uarridx_t) i); + h_bufobj = duk__require_bufobj_value(thr, 4); DUK_ASSERT(h_bufobj != NULL); copy_size = h_bufobj->length; @@ -23796,10 +29273,10 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { } if (h_bufobj->buf != NULL && - DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) { - DUK_MEMCPY((void *) p, - (const void *) DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_bufobj), - copy_size); + DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { + duk_memcpy_unsafe((void *) p, + (const void *) DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufobj), + copy_size); } else { /* Just skip, leaving zeroes in the result. */ ; @@ -23807,24 +29284,19 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { p += copy_size; space_left -= copy_size; - duk_pop(ctx); + duk_pop(thr); } - h_val = duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_val != NULL); + h_val = duk_known_hbuffer(thr, -1); - duk__set_bufobj_buffer(ctx, h_bufres, h_val); - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufres); + duk__set_bufobj_buffer(thr, h_bufres, h_val); + h_bufres->is_typedarray = 1; + DUK_HBUFOBJ_ASSERT_VALID(h_bufres); - duk_pop(ctx); /* pop plain buffer, now reachable through h_bufres */ + duk_pop(thr); /* pop plain buffer, now reachable through h_bufres */ return 1; /* return h_bufres */ } -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* @@ -23852,15 +29324,14 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { #define DUK__FLD_TYPEDARRAY (1 << 5) /* XXX: split into separate functions for each field type? */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { - duk_hthread *thr; - duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx); - duk_small_int_t magic_ftype; - duk_small_int_t magic_bigendian; - duk_small_int_t magic_signed; - duk_small_int_t magic_typedarray; - duk_small_int_t endswap; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_hthread *thr) { + duk_small_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr); + duk_small_uint_t magic_ftype; + duk_small_uint_t magic_bigendian; + duk_small_uint_t magic_signed; + duk_small_uint_t magic_typedarray; + duk_small_uint_t endswap; + duk_hbufobj *h_this; duk_bool_t no_assert; duk_int_t offset_signed; duk_uint_t offset; @@ -23869,15 +29340,12 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { duk_uint8_t *buf; duk_double_union du; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); + magic_ftype = magic & 0x0007U; + magic_bigendian = magic & 0x0008U; + magic_signed = magic & 0x0010U; + magic_typedarray = magic & 0x0020U; - magic_ftype = magic & 0x0007; - magic_bigendian = magic & 0x0008; - magic_signed = magic & 0x0010; - magic_typedarray = magic & 0x0020; - - h_this = duk__require_bufobj_this(ctx); + h_this = duk__require_bufobj_this(thr); /* XXX: very inefficient for plain buffers */ DUK_ASSERT(h_this != NULL); buffer_length = h_this->length; @@ -23889,12 +29357,12 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { if (magic_typedarray) { no_assert = 0; #if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(ctx, 1); /* 1=little endian */ + endswap = !duk_to_boolean(thr, 1); /* 1=little endian */ #else - endswap = duk_to_boolean(ctx, 1); /* 1=little endian */ + endswap = duk_to_boolean(thr, 1); /* 1=little endian */ #endif } else { - no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1); + no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1); #if defined(DUK_USE_INTEGER_LE) endswap = magic_bigendian; #else @@ -23906,7 +29374,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { * This ensures we can add a small byte length (1-8) to the offset in * bound checks and not wrap. */ - offset_signed = duk_to_int(ctx, 0); + offset_signed = duk_to_int(thr, 0); offset = (duk_uint_t) offset_signed; if (offset_signed < 0) { goto fail_bounds; @@ -23914,7 +29382,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { DUK_DDD(DUK_DDDPRINT("readfield, buffer_length=%ld, offset=%ld, no_assert=%d, " "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, " - "endswap=%d", + "endswap=%u", (long) buffer_length, (long) offset, (int) no_assert, (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3), (int) (magic_signed >> 4), (int) endswap)); @@ -23923,12 +29391,12 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { * takes into account the underlying buffer. This value will be * potentially invalidated by any side effect. */ - check_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, buffer_length); + check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length); DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld", (long) buffer_length, (long) check_length)); if (h_this->buf) { - buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this); + buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); } else { /* Neutered. We could go into the switch-case safely with * buf == NULL because check_length == 0. To avoid scanbuild @@ -23947,9 +29415,9 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { } tmp = buf[offset]; if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int8_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -23958,15 +29426,15 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { if (offset + 2U > check_length) { goto fail_bounds; } - DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 2); + duk_memcpy((void *) du.uc, (const void *) (buf + offset), 2); tmp = du.us[0]; if (endswap) { tmp = DUK_BSWAP16(tmp); } if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int16_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int16_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -23975,15 +29443,15 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { if (offset + 4U > check_length) { goto fail_bounds; } - DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 4); + duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4); tmp = du.ui[0]; if (endswap) { tmp = DUK_BSWAP32(tmp); } if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int32_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int32_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -23992,24 +29460,24 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { if (offset + 4U > check_length) { goto fail_bounds; } - DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 4); + duk_memcpy((void *) du.uc, (const void *) (buf + offset), 4); if (endswap) { tmp = du.ui[0]; tmp = DUK_BSWAP32(tmp); du.ui[0] = tmp; } - duk_push_number(ctx, (duk_double_t) du.f[0]); + duk_push_number(thr, (duk_double_t) du.f[0]); break; } case DUK__FLD_DOUBLE: { if (offset + 8U > check_length) { goto fail_bounds; } - DUK_MEMCPY((void *) du.uc, (const void *) (buf + offset), 8); + duk_memcpy((void *) du.uc, (const void *) (buf + offset), 8); if (endswap) { DUK_DBLUNION_BSWAP64(&du); } - duk_push_number(ctx, (duk_double_t) du.d); + duk_push_number(thr, (duk_double_t) du.d); break; } case DUK__FLD_VARINT: { @@ -24027,7 +29495,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { #endif const duk_uint8_t *p; - field_bytelen = duk_get_int(ctx, 1); /* avoid side effects! */ + field_bytelen = duk_get_int(thr, 1); /* avoid side effects! */ if (field_bytelen < 1 || field_bytelen > 6) { goto fail_field_length; } @@ -24062,12 +29530,15 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { } while (i != i_end); if (magic_signed) { - /* Shift to sign extend. */ - shift_tmp = 64 - (field_bytelen * 8); - tmp = (tmp << shift_tmp) >> shift_tmp; + /* Shift to sign extend. Left shift must be unsigned + * to avoid undefined behavior; right shift must be + * signed to sign extend properly. + */ + shift_tmp = (duk_small_uint_t) (64U - (duk_small_uint_t) field_bytelen * 8U); + tmp = (duk_int64_t) ((duk_uint64_t) tmp << shift_tmp) >> shift_tmp; } - duk_push_i64(ctx, tmp); + duk_push_i64(thr, tmp); #else highbyte = p[i]; if (magic_signed && (highbyte & 0x80) != 0) { @@ -24085,7 +29556,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { tmp = (tmp * 256.0) + (duk_double_t) p[i]; } - duk_push_number(ctx, tmp); + duk_push_number(thr, tmp); #endif break; } @@ -24103,30 +29574,23 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { /* Node.js return value for noAssert out-of-bounds reads is * usually (but not always) NaN. Return NaN consistently. */ - duk_push_nan(ctx); + duk_push_nan(thr); return 1; } - - return DUK_RET_RANGE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) /* XXX: split into separate functions for each field type? */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { - duk_hthread *thr; - duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx); - duk_small_int_t magic_ftype; - duk_small_int_t magic_bigendian; - duk_small_int_t magic_signed; - duk_small_int_t magic_typedarray; - duk_small_int_t endswap; - duk_hbufferobject *h_this; +DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_hthread *thr) { + duk_small_uint_t magic = (duk_small_uint_t) duk_get_current_magic(thr); + duk_small_uint_t magic_ftype; + duk_small_uint_t magic_bigendian; + duk_small_uint_t magic_signed; + duk_small_uint_t magic_typedarray; + duk_small_uint_t endswap; + duk_hbufobj *h_this; duk_bool_t no_assert; duk_int_t offset_signed; duk_uint_t offset; @@ -24136,16 +29600,13 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { duk_double_union du; duk_int_t nbytes = 0; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - - magic_ftype = magic & 0x0007; - magic_bigendian = magic & 0x0008; - magic_signed = magic & 0x0010; - magic_typedarray = magic & 0x0020; + magic_ftype = magic & 0x0007U; + magic_bigendian = magic & 0x0008U; + magic_signed = magic & 0x0010U; + magic_typedarray = magic & 0x0020U; DUK_UNREF(magic_signed); - h_this = duk__require_bufobj_this(ctx); + h_this = duk__require_bufobj_this(thr); /* XXX: very inefficient for plain buffers */ DUK_ASSERT(h_this != NULL); buffer_length = h_this->length; @@ -24157,13 +29618,13 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (magic_typedarray) { no_assert = 0; #if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(ctx, 2); /* 1=little endian */ + endswap = !duk_to_boolean(thr, 2); /* 1=little endian */ #else - endswap = duk_to_boolean(ctx, 2); /* 1=little endian */ + endswap = duk_to_boolean(thr, 2); /* 1=little endian */ #endif - duk_swap(ctx, 0, 1); /* offset/value order different from Node.js */ + duk_swap(thr, 0, 1); /* offset/value order different from Node.js */ } else { - no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2); + no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2); #if defined(DUK_USE_INTEGER_LE) endswap = magic_bigendian; #else @@ -24175,17 +29636,17 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { * This ensures we can add a small byte length (1-8) to the offset in * bound checks and not wrap. */ - offset_signed = duk_to_int(ctx, 1); + offset_signed = duk_to_int(thr, 1); offset = (duk_uint_t) offset_signed; /* We need 'nbytes' even for a failed offset; return value must be * (offset + nbytes) even when write fails due to invalid offset. */ if (magic_ftype != DUK__FLD_VARINT) { - DUK_ASSERT(magic_ftype >= 0 && magic_ftype < (duk_small_int_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t))); + DUK_ASSERT(magic_ftype < (duk_small_uint_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t))); nbytes = duk__buffer_nbytes_from_fldtype[magic_ftype]; } else { - nbytes = duk_get_int(ctx, 2); + nbytes = duk_get_int(thr, 2); if (nbytes < 1 || nbytes > 6) { goto fail_field_length; } @@ -24199,8 +29660,8 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, " "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, " - "endswap=%d", - duk_get_tval(ctx, 0), (long) buffer_length, (long) offset, (int) no_assert, + "endswap=%u", + duk_get_tval(thr, 0), (long) buffer_length, (long) offset, (int) no_assert, (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3), (int) (magic_signed >> 4), (int) endswap)); @@ -24208,18 +29669,18 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { * the field type specific coercion below can't have side effects * that would invalidate check_length. */ - duk_to_number(ctx, 0); + duk_to_number(thr, 0); /* Update 'buffer_length' to be the effective, safe limit which * takes into account the underlying buffer. This value will be * potentially invalidated by any side effect. */ - check_length = DUK_HBUFFEROBJECT_CLAMP_BYTELENGTH(h_this, buffer_length); + check_length = DUK_HBUFOBJ_CLAMP_BYTELENGTH(h_this, buffer_length); DUK_DDD(DUK_DDDPRINT("buffer_length=%ld, check_length=%ld", (long) buffer_length, (long) check_length)); if (h_this->buf) { - buf = DUK_HBUFFEROBJECT_GET_SLICE_BASE(thr->heap, h_this); + buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this); } else { /* Neutered. We could go into the switch-case safely with * buf == NULL because check_length == 0. To avoid scanbuild @@ -24236,7 +29697,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { goto fail_bounds; } /* sign doesn't matter when writing */ - buf[offset] = (duk_uint8_t) duk_to_uint32(ctx, 0); + buf[offset] = (duk_uint8_t) duk_to_uint32(thr, 0); break; } case DUK__FLD_16BIT: { @@ -24244,13 +29705,13 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (offset + 2U > check_length) { goto fail_bounds; } - tmp = (duk_uint16_t) duk_to_uint32(ctx, 0); + tmp = (duk_uint16_t) duk_to_uint32(thr, 0); if (endswap) { tmp = DUK_BSWAP16(tmp); } du.us[0] = tmp; /* sign doesn't matter when writing */ - DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 2); + duk_memcpy((void *) (buf + offset), (const void *) du.uc, 2); break; } case DUK__FLD_32BIT: { @@ -24258,13 +29719,13 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (offset + 4U > check_length) { goto fail_bounds; } - tmp = (duk_uint32_t) duk_to_uint32(ctx, 0); + tmp = (duk_uint32_t) duk_to_uint32(thr, 0); if (endswap) { tmp = DUK_BSWAP32(tmp); } du.ui[0] = tmp; /* sign doesn't matter when writing */ - DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 4); + duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4); break; } case DUK__FLD_FLOAT: { @@ -24272,26 +29733,26 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (offset + 4U > check_length) { goto fail_bounds; } - du.f[0] = (duk_float_t) duk_to_number(ctx, 0); + du.f[0] = (duk_float_t) duk_to_number(thr, 0); if (endswap) { tmp = du.ui[0]; tmp = DUK_BSWAP32(tmp); du.ui[0] = tmp; } /* sign doesn't matter when writing */ - DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 4); + duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4); break; } case DUK__FLD_DOUBLE: { if (offset + 8U > check_length) { goto fail_bounds; } - du.d = (duk_double_t) duk_to_number(ctx, 0); + du.d = (duk_double_t) duk_to_number(thr, 0); if (endswap) { DUK_DBLUNION_BSWAP64(&du); } /* sign doesn't matter when writing */ - DUK_MEMCPY((void *) (buf + offset), (const void *) du.uc, 8); + duk_memcpy((void *) (buf + offset), (const void *) du.uc, 8); break; } case DUK__FLD_VARINT: { @@ -24336,7 +29797,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { */ #if defined(DUK_USE_64BIT_OPS) - tmp = (duk_int64_t) duk_to_number(ctx, 0); + tmp = (duk_int64_t) duk_to_number(thr, 0); p = (duk_uint8_t *) (buf + offset); do { i += i_step; @@ -24345,7 +29806,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { tmp = tmp >> 8; /* unnecessary shift for last byte */ } while (i != i_end); #else - tmp = duk_to_number(ctx, 0); + tmp = duk_to_number(thr, 0); p = (duk_uint8_t *) (buf + offset); do { i += i_step; @@ -24367,11 +29828,11 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { */ if (magic_typedarray) { /* For TypedArrays 'undefined' return value is specified - * by ES6 (matches V8). + * by ES2015 (matches V8). */ return 0; } - duk_push_uint(ctx, offset + nbytes); + duk_push_uint(thr, offset + (duk_uint_t) nbytes); return 1; fail_neutered: @@ -24387,28 +29848,1894 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (magic_typedarray) { return 0; } - duk_push_uint(ctx, offset + nbytes); + duk_push_uint(thr, offset + (duk_uint_t) nbytes); return 1; } - return DUK_RET_RANGE_ERROR; -} -#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; + DUK_DCERROR_RANGE_INVALID_ARGS(thr); } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -#undef DUK__FLD_8BIT -#undef DUK__FLD_16BIT -#undef DUK__FLD_32BIT -#undef DUK__FLD_FLOAT -#undef DUK__FLD_DOUBLE -#undef DUK__FLD_VARINT -#undef DUK__FLD_BIGENDIAN -#undef DUK__FLD_SIGNED -#undef DUK__FLD_TYPEDARRAY -#line 1 "duk_bi_date.c" +/* + * Accessors for .buffer, .byteLength, .byteOffset + */ + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_hthread *thr, duk_hbuffer *h_buf) { + duk_hbufobj *h_res; + + h_res = duk_push_bufobj_raw(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), + DUK_BIDX_ARRAYBUFFER_PROTOTYPE); + DUK_ASSERT(h_res != NULL); + DUK_UNREF(h_res); + + duk__set_bufobj_buffer(thr, h_res, h_buf); + DUK_HBUFOBJ_ASSERT_VALID(h_res); + DUK_ASSERT(h_res->buf_prop == NULL); + return h_res; +} + +DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) { + duk_hbufobj *h_bufobj; + + h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); + DUK_ASSERT(h_bufobj != NULL); + if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { + DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for plain buffer")); + (void) duk__autospawn_arraybuffer(thr, (duk_hbuffer *) h_bufobj); + return 1; + } else { + if (h_bufobj->buf_prop == NULL && + DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufobj) != DUK_HOBJECT_CLASS_ARRAYBUFFER && + h_bufobj->buf != NULL) { + duk_hbufobj *h_arrbuf; + + DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for typed array or DataView")); + h_arrbuf = duk__autospawn_arraybuffer(thr, h_bufobj->buf); + + if (h_bufobj->buf_prop == NULL) { + /* Must recheck buf_prop, in case ArrayBuffer + * alloc had a side effect which already filled + * it! + */ + + /* Set ArrayBuffer's .byteOffset and .byteLength based + * on the view so that Arraybuffer[view.byteOffset] + * matches view[0]. + */ + h_arrbuf->offset = 0; + DUK_ASSERT(h_bufobj->offset + h_bufobj->length >= h_bufobj->offset); /* Wrap check on creation. */ + h_arrbuf->length = h_bufobj->offset + h_bufobj->length; + DUK_ASSERT(h_arrbuf->buf_prop == NULL); + + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; + DUK_HBUFOBJ_INCREF(thr, h_arrbuf); /* Now reachable and accounted for. */ + } + + /* Left on stack; pushed for the second time below (OK). */ + } + if (h_bufobj->buf_prop) { + duk_push_hobject(thr, h_bufobj->buf_prop); + return 1; + } + } + return 0; +} + +DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) { + duk_hbufobj *h_bufobj; + + h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); + DUK_ASSERT(h_bufobj != NULL); + if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { + duk_push_uint(thr, 0); + } else { + /* If neutered must return 0; offset is zeroed during + * neutering. + */ + duk_push_uint(thr, h_bufobj->offset); + } + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) { + duk_hbufobj *h_bufobj; + + h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/); + DUK_ASSERT(h_bufobj != NULL); + if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { + duk_hbuffer *h_buf; + + h_buf = (duk_hbuffer *) h_bufobj; + DUK_ASSERT(DUK_HBUFFER_GET_SIZE(h_buf) <= DUK_UINT_MAX); /* Buffer limits. */ + duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf)); + } else { + /* If neutered must return 0; length is zeroed during + * neutering. + */ + duk_push_uint(thr, h_bufobj->length); + } + return 1; +} +#else /* DUK_USE_BUFFEROBJECT_SUPPORT */ +/* No .buffer getter without ArrayBuffer support. */ +#if 0 +DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) { + return 0; +} +#endif + +DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) { + duk_push_uint(thr, 0); + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) { + duk_hbuffer *h_buf; + + /* XXX: helper? */ + duk_push_this(thr); + h_buf = duk_require_hbuffer(thr, -1); + duk_push_uint(thr, DUK_HBUFFER_GET_SIZE(h_buf)); + return 1; +} +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + +/* automatic undefs */ +#undef DUK__BUFOBJ_FLAG_PROMOTE +#undef DUK__BUFOBJ_FLAG_THROW +#undef DUK__FLD_16BIT +#undef DUK__FLD_32BIT +#undef DUK__FLD_8BIT +#undef DUK__FLD_BIGENDIAN +#undef DUK__FLD_DOUBLE +#undef DUK__FLD_FLOAT +#undef DUK__FLD_SIGNED +#undef DUK__FLD_TYPEDARRAY +#undef DUK__FLD_VARINT +/* + * CBOR bindings. + * + * http://cbor.io/ + * https://tools.ietf.org/html/rfc7049 + */ + +/* #include duk_internal.h -> already included */ + +#if defined(DUK_USE_CBOR_SUPPORT) + +/* #define DUK_CBOR_STRESS */ + +/* Default behavior for encoding strings: use CBOR text string if string + * is UTF-8 compatible, otherwise use CBOR byte string. These defines + * can be used to force either type for all strings. Using text strings + * for non-UTF-8 data is technically invalid CBOR. + */ +/* #define DUK_CBOR_TEXT_STRINGS */ +/* #define DUK_CBOR_BYTE_STRINGS */ + +/* Misc. defines. */ +/* #define DUK_CBOR_PREFER_SIZE */ +/* #define DUK_CBOR_DOUBLE_AS_IS */ +/* #define DUK_CBOR_DECODE_FASTPATH */ + +typedef struct { + duk_hthread *thr; + duk_uint8_t *ptr; + duk_uint8_t *buf; + duk_uint8_t *buf_end; + duk_size_t len; + duk_idx_t idx_buf; + duk_uint_t recursion_depth; + duk_uint_t recursion_limit; +} duk_cbor_encode_context; + +typedef struct { + duk_hthread *thr; + const duk_uint8_t *buf; + duk_size_t off; + duk_size_t len; + duk_uint_t recursion_depth; + duk_uint_t recursion_limit; +} duk_cbor_decode_context; + +DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx); +DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx); + +/* + * Misc + */ + +DUK_LOCAL duk_uint32_t duk__cbor_double_to_uint32(double d) { + /* Out of range casts are undefined behavior, so caller must avoid. */ + DUK_ASSERT(d >= 0.0 && d <= 4294967295.0); + return (duk_uint32_t) d; +} + +/* + * Encoding + */ + +DUK_LOCAL void duk__cbor_encode_error(duk_cbor_encode_context *enc_ctx) { + (void) duk_type_error(enc_ctx->thr, "cbor encode error"); +} + +DUK_LOCAL void duk__cbor_encode_req_stack(duk_cbor_encode_context *enc_ctx) { + duk_require_stack(enc_ctx->thr, 4); +} + +DUK_LOCAL void duk__cbor_encode_objarr_entry(duk_cbor_encode_context *enc_ctx) { + duk_hthread *thr = enc_ctx->thr; + + /* Native stack check in object/array recursion. */ + duk_native_stack_check(thr); + + /* When working with deeply recursive structures, this is important + * to ensure there's no effective depth limit. + */ + duk__cbor_encode_req_stack(enc_ctx); + + DUK_ASSERT(enc_ctx->recursion_depth <= enc_ctx->recursion_limit); + if (enc_ctx->recursion_depth >= enc_ctx->recursion_limit) { + DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT); + DUK_WO_NORETURN(return;); + } + enc_ctx->recursion_depth++; +} + +DUK_LOCAL void duk__cbor_encode_objarr_exit(duk_cbor_encode_context *enc_ctx) { + DUK_ASSERT(enc_ctx->recursion_depth > 0); + enc_ctx->recursion_depth--; +} + +/* Check that a size_t is in uint32 range to avoid out-of-range casts. */ +DUK_LOCAL void duk__cbor_encode_sizet_uint32_check(duk_cbor_encode_context *enc_ctx, duk_size_t len) { + if (DUK_UNLIKELY(sizeof(duk_size_t) > sizeof(duk_uint32_t) && len > (duk_size_t) DUK_UINT32_MAX)) { + duk__cbor_encode_error(enc_ctx); + } +} + +DUK_LOCAL DUK_NOINLINE void duk__cbor_encode_ensure_slowpath(duk_cbor_encode_context *enc_ctx, duk_size_t len) { + duk_size_t oldlen; + duk_size_t minlen; + duk_size_t newlen; + duk_uint8_t *p_new; + duk_size_t old_data_len; + + DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf); + DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->ptr); + DUK_ASSERT(enc_ctx->buf_end >= enc_ctx->buf); + + /* Overflow check. + * + * Limit example: 0xffffffffUL / 2U = 0x7fffffffUL, we reject >= 0x80000000UL. + */ + oldlen = enc_ctx->len; + minlen = oldlen + len; + if (DUK_UNLIKELY(oldlen > DUK_SIZE_MAX / 2U || minlen < oldlen)) { + duk__cbor_encode_error(enc_ctx); + } + +#if defined(DUK_CBOR_STRESS) + newlen = oldlen + 1U; +#else + newlen = oldlen * 2U; +#endif + DUK_ASSERT(newlen >= oldlen); + + if (minlen > newlen) { + newlen = minlen; + } + DUK_ASSERT(newlen >= oldlen); + DUK_ASSERT(newlen >= minlen); + DUK_ASSERT(newlen > 0U); + + DUK_DD(DUK_DDPRINT("cbor encode buffer resized to %ld", (long) newlen)); + + p_new = (duk_uint8_t *) duk_resize_buffer(enc_ctx->thr, enc_ctx->idx_buf, newlen); + DUK_ASSERT(p_new != NULL); + old_data_len = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); + enc_ctx->buf = p_new; + enc_ctx->buf_end = p_new + newlen; + enc_ctx->ptr = p_new + old_data_len; + enc_ctx->len = newlen; +} + +DUK_LOCAL DUK_INLINE void duk__cbor_encode_ensure(duk_cbor_encode_context *enc_ctx, duk_size_t len) { + if (DUK_LIKELY((duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr) >= len)) { + return; + } + duk__cbor_encode_ensure_slowpath(enc_ctx, len); +} + +DUK_LOCAL duk_size_t duk__cbor_get_reserve(duk_cbor_encode_context *enc_ctx) { + DUK_ASSERT(enc_ctx->ptr >= enc_ctx->buf); + DUK_ASSERT(enc_ctx->ptr <= enc_ctx->buf_end); + return (duk_size_t) (enc_ctx->buf_end - enc_ctx->ptr); +} + +DUK_LOCAL void duk__cbor_encode_uint32(duk_cbor_encode_context *enc_ctx, duk_uint32_t u, duk_uint8_t base) { + duk_uint8_t *p; + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 4); + + p = enc_ctx->ptr; + if (DUK_LIKELY(u <= 23U)) { + *p++ = (duk_uint8_t) (base + (duk_uint8_t) u); + } else if (u <= 0xffUL) { + *p++ = base + 0x18U; + *p++ = (duk_uint8_t) u; + } else if (u <= 0xffffUL) { + *p++ = base + 0x19U; + DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) u); + } else { + *p++ = base + 0x1aU; + DUK_RAW_WRITEINC_U32_BE(p, u); + } + enc_ctx->ptr = p; +} + +#if defined(DUK_CBOR_DOUBLE_AS_IS) +DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { + duk_uint8_t *p; + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + p = enc_ctx->ptr; + *p++ = 0xfbU; + DUK_RAW_WRITEINC_DOUBLE_BE(p, d); + p += 8; + enc_ctx->ptr = p; +} +#else /* DUK_CBOR_DOUBLE_AS_IS */ +DUK_LOCAL void duk__cbor_encode_double_fp(duk_cbor_encode_context *enc_ctx, double d) { + duk_double_union u; + duk_uint16_t u16; + duk_int16_t expt; + duk_uint8_t *p; + + DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO); + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + /* Organize into little endian (no-op if platform is little endian). */ + u.d = d; + duk_dblunion_host_to_little(&u); + + /* Check if 'd' can represented as a normal half-float. + * Denormal half-floats could also be used, but that check + * isn't done now (denormal half-floats are decoded of course). + * So just check exponent range and that at most 10 significant + * bits (excluding implicit leading 1) are used in 'd'. + */ + u16 = (((duk_uint16_t) u.uc[7]) << 8) | ((duk_uint16_t) u.uc[6]); + expt = (duk_int16_t) ((u16 & 0x7ff0U) >> 4) - 1023; + + if (expt >= -14 && expt <= 15) { + /* Half-float normal exponents (excl. denormals). + * + * 7 6 5 4 3 2 1 0 (LE index) + * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm + * half: seeeee mmmm mmmmmm00 00000000 00000000 00000000 00000000 00000000 + */ + duk_bool_t use_half_float; + + use_half_float = + (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && u.uc[3] == 0 && + u.uc[4] == 0 && (u.uc[5] & 0x03U) == 0); + + if (use_half_float) { + duk_uint32_t t; + + expt += 15; + t = (duk_uint32_t) (u.uc[7] & 0x80U) << 8; + t += (duk_uint32_t) expt << 10; + t += ((duk_uint32_t) u.uc[6] & 0x0fU) << 6; + t += ((duk_uint32_t) u.uc[5]) >> 2; + + /* seeeeemm mmmmmmmm */ + p = enc_ctx->ptr; + *p++ = 0xf9U; + DUK_RAW_WRITEINC_U16_BE(p, (duk_uint16_t) t); + enc_ctx->ptr = p; + return; + } + } + + /* Same check for plain float. Also no denormal support here. */ + if (expt >= -126 && expt <= 127) { + /* Float normal exponents (excl. denormals). + * + * double: seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm + * float: seeee eeeemmmm mmmmmmmm mmmmmmmm mmm00000 00000000 00000000 00000000 + */ + duk_bool_t use_float; + duk_float_t d_float; + + /* We could do this explicit mantissa check, but doing + * a double-float-double cast is fine because we've + * already verified that the exponent is in range so + * that the narrower cast is not undefined behavior. + */ +#if 0 + use_float = + (u.uc[0] == 0 && u.uc[1] == 0 && u.uc[2] == 0 && (u.uc[3] & 0xe0U) == 0); +#endif + d_float = (duk_float_t) d; + use_float = duk_double_equals((duk_double_t) d_float, d); + if (use_float) { + p = enc_ctx->ptr; + *p++ = 0xfaU; + DUK_RAW_WRITEINC_FLOAT_BE(p, d_float); + enc_ctx->ptr = p; + return; + } + } + + /* Special handling for NaN and Inf which we want to encode as + * half-floats. They share the same (maximum) exponent. + */ + if (expt == 1024) { + DUK_ASSERT(DUK_ISNAN(d) || DUK_ISINF(d)); + p = enc_ctx->ptr; + *p++ = 0xf9U; + if (DUK_ISNAN(d)) { + /* Shortest NaN encoding is using a half-float. Lose the + * exact NaN bits in the process. IEEE double would be + * 7ff8 0000 0000 0000, i.e. a quiet NaN in most architectures + * (https://en.wikipedia.org/wiki/NaN#Encoding). The + * equivalent half float is 7e00. + */ + *p++ = 0x7eU; + } else { + /* Shortest +/- Infinity encoding is using a half-float. */ + if (DUK_SIGNBIT(d)) { + *p++ = 0xfcU; + } else { + *p++ = 0x7cU; + } + } + *p++ = 0x00U; + enc_ctx->ptr = p; + return; + } + + /* Cannot use half-float or float, encode as full IEEE double. */ + p = enc_ctx->ptr; + *p++ = 0xfbU; + DUK_RAW_WRITEINC_DOUBLE_BE(p, d); + enc_ctx->ptr = p; +} + +DUK_LOCAL void duk__cbor_encode_double(duk_cbor_encode_context *enc_ctx, double d) { + duk_uint8_t *p; + double d_floor; + + /* Integers and floating point values of all types are conceptually + * equivalent in CBOR. Try to always choose the shortest encoding + * which is not always immediately obvious. For example, NaN and Inf + * can be most compactly represented as a half-float (assuming NaN + * bits are not preserved), and 0x1'0000'0000 as a single precision + * float. Shortest forms in preference order (prefer integer over + * float when equal length): + * + * uint 1 byte [0,23] (not -0) + * sint 1 byte [-24,-1] + * uint+1 2 bytes [24,255] + * sint+1 2 bytes [-256,-25] + * uint+2 3 bytes [256,65535] + * sint+2 3 bytes [-65536,-257] + * half-float 3 bytes -0, NaN, +/- Infinity, range [-65504,65504] + * uint+4 5 bytes [65536,4294967295] + * sint+4 5 bytes [-4294967296,-258] + * float 5 bytes range [-(1 - 2^(-24)) * 2^128, (1 - 2^(-24)) * 2^128] + * uint+8 9 bytes [4294967296,18446744073709551615] + * sint+8 9 bytes [-18446744073709551616,-4294967297] + * double 9 bytes + * + * For whole numbers (compatible with integers): + * - 1-byte or 2-byte uint/sint representation is preferred for + * [-256,255]. + * - 3-byte uint/sint is preferred for [-65536,65535]. Half floats + * are never preferred because they have the same length. + * - 5-byte uint/sint is preferred for [-4294967296,4294967295]. + * Single precision floats are never preferred, and half-floats + * don't reach above the 3-byte uint/sint range so they're never + * preferred. + * - So, for all integers up to signed/unsigned 32-bit range the + * preferred encoding is always an integer uint/sint. + * - For integers above 32 bits the situation is more complicated. + * Half-floats are never useful for them because of their limited + * range, but IEEE single precision floats (5 bytes encoded) can + * represent some integers between the 32-bit and 64-bit ranges + * which require 9 bytes as a uint/sint. + * + * For floating point values not compatible with integers, the + * preferred encoding is quite clear: + * - For +Inf/-Inf use half-float. + * - For NaN use a half-float, assuming NaN bits ("payload") is + * not worth preserving. Duktape doesn't in general guarantee + * preservation of the NaN payload so using a half-float seems + * consistent with that. + * - For remaining values, prefer the shortest form which doesn't + * lose any precision. For normal half-floats and single precision + * floats this is simple: just check exponent and mantissa bits + * using a fixed mask. For denormal half-floats and single + * precision floats the check is a bit more complicated: a normal + * IEEE double can sometimes be represented as a denormal + * half-float or single precision float. + * + * https://en.wikipedia.org/wiki/Half-precision_floating-point_format#IEEE_754_half-precision_binary_floating-point_format:_binary16 + */ + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + /* Most important path is integers. The floor() test will be true + * for Inf too (but not NaN). + */ + d_floor = DUK_FLOOR(d); /* identity if d is +/- 0.0, NaN, or +/- Infinity */ + if (DUK_LIKELY(duk_double_equals(d_floor, d) != 0)) { + DUK_ASSERT(!DUK_ISNAN(d)); /* NaN == NaN compares false. */ + if (DUK_SIGNBIT(d)) { + if (d >= -4294967296.0) { + d = -1.0 - d; + if (d >= 0.0) { + DUK_ASSERT(d >= 0.0); + duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x20U); + return; + } + + /* Input was negative zero, d == -1.0 < 0.0. + * Shortest -0 is using half-float. + */ + p = enc_ctx->ptr; + *p++ = 0xf9U; + *p++ = 0x80U; + *p++ = 0x00U; + enc_ctx->ptr = p; + return; + } + } else { + if (d <= 4294967295.0) { + /* Positive zero needs no special handling. */ + DUK_ASSERT(d >= 0.0); + duk__cbor_encode_uint32(enc_ctx, duk__cbor_double_to_uint32(d), 0x00U); + return; + } + } + } + + /* 64-bit integers are not supported at present. So + * we also don't need to deal with choosing between a + * 64-bit uint/sint representation vs. IEEE double or + * float. + */ + + DUK_ASSERT(DUK_FPCLASSIFY(d) != DUK_FP_ZERO); + duk__cbor_encode_double_fp(enc_ctx, d); +} +#endif /* DUK_CBOR_DOUBLE_AS_IS */ + +DUK_LOCAL void duk__cbor_encode_string_top(duk_cbor_encode_context *enc_ctx) { + const duk_uint8_t *str; + duk_size_t len; + duk_uint8_t *p; + + /* CBOR differentiates between UTF-8 text strings and byte strings. + * Text strings MUST be valid UTF-8, so not all Duktape strings can + * be encoded as valid CBOR text strings. Possible behaviors: + * + * 1. Use text string when input is valid UTF-8, otherwise use + * byte string (maybe tagged to indicate it was an extended + * UTF-8 string). + * 2. Always use text strings, but sanitize input string so that + * invalid UTF-8 is replaced with U+FFFD for example. Combine + * surrogates whenever possible. + * 3. Always use byte strings. This is simple and produces valid + * CBOR, but isn't ideal for interoperability. + * 4. Always use text strings, even for invalid UTF-8 such as + * codepoints in the surrogate pair range. This is simple but + * produces technically invalid CBOR for non-UTF-8 strings which + * may affect interoperability. + * + * Current default is 1; can be changed with defines. + */ + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + str = (const duk_uint8_t *) duk_require_lstring(enc_ctx->thr, -1, &len); + if (duk_is_symbol(enc_ctx->thr, -1)) { + /* Symbols, encode as an empty table for now. This matches + * the behavior of cbor-js. + * + * XXX: Maybe encode String() coercion with a tag? + * XXX: Option to keep enough information to recover + * Symbols when decoding (this is not always desirable). + */ + p = enc_ctx->ptr; + *p++ = 0xa0U; + enc_ctx->ptr = p; + return; + } + + duk__cbor_encode_sizet_uint32_check(enc_ctx, len); +#if defined(DUK_CBOR_TEXT_STRINGS) + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x60U); +#elif defined(DUK_CBOR_BYTE_STRINGS) + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); +#else + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, + (DUK_LIKELY(duk_unicode_is_utf8_compatible(str, len) != 0) ? 0x60U : 0x40U)); +#endif + duk__cbor_encode_ensure(enc_ctx, len); + p = enc_ctx->ptr; + duk_memcpy((void *) p, (const void *) str, len); + p += len; + enc_ctx->ptr = p; +} + +DUK_LOCAL void duk__cbor_encode_object(duk_cbor_encode_context *enc_ctx) { + duk_uint8_t *buf; + duk_size_t len; + duk_uint8_t *p; + duk_size_t i; + duk_size_t off_ib; + duk_uint32_t count; + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + duk__cbor_encode_objarr_entry(enc_ctx); + + /* XXX: Support for specific built-ins like Date and RegExp. */ + if (duk_is_array(enc_ctx->thr, -1)) { + /* Shortest encoding for arrays >= 256 in length is actually + * the indefinite length one (3 or more bytes vs. 2 bytes). + * We still use the definite length version because it is + * more decoding friendly. + */ + len = duk_get_length(enc_ctx->thr, -1); + duk__cbor_encode_sizet_uint32_check(enc_ctx, len); + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x80U); + for (i = 0; i < len; i++) { + duk_get_prop_index(enc_ctx->thr, -1, (duk_uarridx_t) i); + duk__cbor_encode_value(enc_ctx); + } + } else if (duk_is_buffer_data(enc_ctx->thr, -1)) { + /* XXX: Tag buffer data? + * XXX: Encode typed arrays as integer arrays rather + * than buffer data as is? + */ + buf = (duk_uint8_t *) duk_require_buffer_data(enc_ctx->thr, -1, &len); + duk__cbor_encode_sizet_uint32_check(enc_ctx, len); + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); + duk__cbor_encode_ensure(enc_ctx, len); + p = enc_ctx->ptr; + duk_memcpy_unsafe((void *) p, (const void *) buf, len); + p += len; + enc_ctx->ptr = p; + } else { + /* We don't know the number of properties in advance + * but would still like to encode at least small + * objects without indefinite length. Emit an + * indefinite length byte initially, and if the final + * property count is small enough to also fit in one + * byte, backpatch it later. Otherwise keep the + * indefinite length. This works well up to 23 + * properties which is practical and good enough. + */ + off_ib = (duk_size_t) (enc_ctx->ptr - enc_ctx->buf); /* XXX: get_offset? */ + count = 0U; + p = enc_ctx->ptr; + *p++ = 0xa0U + 0x1fU; /* indefinite length */ + enc_ctx->ptr = p; + duk_enum(enc_ctx->thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY); + while (duk_next(enc_ctx->thr, -1, 1 /*get_value*/)) { + duk_insert(enc_ctx->thr, -2); /* [ ... key value ] -> [ ... value key ] */ + duk__cbor_encode_value(enc_ctx); + duk__cbor_encode_value(enc_ctx); + count++; + if (count == 0U) { + duk__cbor_encode_error(enc_ctx); + } + } + duk_pop(enc_ctx->thr); + if (count <= 0x17U) { + DUK_ASSERT(off_ib < enc_ctx->len); + enc_ctx->buf[off_ib] = 0xa0U + (duk_uint8_t) count; + } else { + duk__cbor_encode_ensure(enc_ctx, 1); + p = enc_ctx->ptr; + *p++ = 0xffU; /* break */ + enc_ctx->ptr = p; + } + } + + duk__cbor_encode_objarr_exit(enc_ctx); +} + +DUK_LOCAL void duk__cbor_encode_buffer(duk_cbor_encode_context *enc_ctx) { + duk_uint8_t *buf; + duk_size_t len; + duk_uint8_t *p; + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + /* Tag buffer data? */ + buf = (duk_uint8_t *) duk_require_buffer(enc_ctx->thr, -1, &len); + duk__cbor_encode_sizet_uint32_check(enc_ctx, len); + duk__cbor_encode_uint32(enc_ctx, (duk_uint32_t) len, 0x40U); + duk__cbor_encode_ensure(enc_ctx, len); + p = enc_ctx->ptr; + duk_memcpy_unsafe((void *) p, (const void *) buf, len); + p += len; + enc_ctx->ptr = p; +} + +DUK_LOCAL void duk__cbor_encode_pointer(duk_cbor_encode_context *enc_ctx) { + /* Pointers (void *) are challenging to encode. They can't + * be relied to be even 64-bit integer compatible (there are + * pointer models larger than that), nor can floats encode + * them. They could be encoded as strings (%p format) but + * that's not portable. They could be encoded as direct memory + * representations. Recovering pointers is non-portable in any + * case but it would be nice to be able to detect and recover + * compatible pointers. + * + * For now, encode as "(%p)" string, matching JX. There doesn't + * seem to be an appropriate tag, so pointers don't currently + * survive a CBOR encode/decode roundtrip intact. + */ + const char *ptr; + + ptr = duk_to_string(enc_ctx->thr, -1); + DUK_ASSERT(ptr != NULL); + duk_push_sprintf(enc_ctx->thr, "(%s)", ptr); + duk_remove(enc_ctx->thr, -2); + duk__cbor_encode_string_top(enc_ctx); +} + +DUK_LOCAL void duk__cbor_encode_lightfunc(duk_cbor_encode_context *enc_ctx) { + duk_uint8_t *p; + + /* Caller must ensure space. */ + DUK_ASSERT(duk__cbor_get_reserve(enc_ctx) >= 1 + 8); + + /* For now encode as an empty object. */ + p = enc_ctx->ptr; + *p++ = 0xa0U; + enc_ctx->ptr = p; +} + +DUK_LOCAL void duk__cbor_encode_value(duk_cbor_encode_context *enc_ctx) { + duk_uint8_t *p; + + /* Encode/decode cycle currently loses some type information. + * This can be improved by registering custom tags with IANA. + */ + + /* Reserve space for up to 64-bit types (1 initial byte + 8 + * followup bytes). This allows encoding of integers, floats, + * string/buffer length fields, etc without separate checks + * in each code path. + */ + duk__cbor_encode_ensure(enc_ctx, 1 + 8); + + switch (duk_get_type(enc_ctx->thr, -1)) { + case DUK_TYPE_UNDEFINED: { + p = enc_ctx->ptr; + *p++ = 0xf7; + enc_ctx->ptr = p; + break; + } + case DUK_TYPE_NULL: { + p = enc_ctx->ptr; + *p++ = 0xf6; + enc_ctx->ptr = p; + break; + } + case DUK_TYPE_BOOLEAN: { + duk_uint8_t u8 = duk_get_boolean(enc_ctx->thr, -1) ? 0xf5U : 0xf4U; + p = enc_ctx->ptr; + *p++ = u8; + enc_ctx->ptr = p; + break; + } + case DUK_TYPE_NUMBER: { + duk__cbor_encode_double(enc_ctx, duk_get_number(enc_ctx->thr, -1)); + break; + } + case DUK_TYPE_STRING: { + duk__cbor_encode_string_top(enc_ctx); + break; + } + case DUK_TYPE_OBJECT: { + duk__cbor_encode_object(enc_ctx); + break; + } + case DUK_TYPE_BUFFER: { + duk__cbor_encode_buffer(enc_ctx); + break; + } + case DUK_TYPE_POINTER: { + duk__cbor_encode_pointer(enc_ctx); + break; + } + case DUK_TYPE_LIGHTFUNC: { + duk__cbor_encode_lightfunc(enc_ctx); + break; + } + case DUK_TYPE_NONE: + default: + goto fail; + } + + duk_pop(enc_ctx->thr); + return; + + fail: + duk__cbor_encode_error(enc_ctx); +} + +/* + * Decoding + */ + +DUK_LOCAL void duk__cbor_decode_error(duk_cbor_decode_context *dec_ctx) { + (void) duk_type_error(dec_ctx->thr, "cbor decode error"); +} + +DUK_LOCAL void duk__cbor_decode_req_stack(duk_cbor_decode_context *dec_ctx) { + duk_require_stack(dec_ctx->thr, 4); +} + +DUK_LOCAL void duk__cbor_decode_objarr_entry(duk_cbor_decode_context *dec_ctx) { + duk_hthread *thr = dec_ctx->thr; + + /* Native stack check in object/array recursion. */ + duk_native_stack_check(thr); + + duk__cbor_decode_req_stack(dec_ctx); + + DUK_ASSERT(dec_ctx->recursion_depth <= dec_ctx->recursion_limit); + if (dec_ctx->recursion_depth >= dec_ctx->recursion_limit) { + DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT); + DUK_WO_NORETURN(return;); + } + dec_ctx->recursion_depth++; +} + +DUK_LOCAL void duk__cbor_decode_objarr_exit(duk_cbor_decode_context *dec_ctx) { + DUK_ASSERT(dec_ctx->recursion_depth > 0); + dec_ctx->recursion_depth--; +} + +DUK_LOCAL duk_uint8_t duk__cbor_decode_readbyte(duk_cbor_decode_context *dec_ctx) { + DUK_ASSERT(dec_ctx->off <= dec_ctx->len); + if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 1U)) { + duk__cbor_decode_error(dec_ctx); + } + return dec_ctx->buf[dec_ctx->off++]; +} + +DUK_LOCAL duk_uint16_t duk__cbor_decode_read_u16(duk_cbor_decode_context *dec_ctx) { + duk_uint16_t res; + + DUK_ASSERT(dec_ctx->off <= dec_ctx->len); + if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 2U)) { + duk__cbor_decode_error(dec_ctx); + } + res = DUK_RAW_READ_U16_BE(dec_ctx->buf + dec_ctx->off); + dec_ctx->off += 2; + return res; +} + +DUK_LOCAL duk_uint32_t duk__cbor_decode_read_u32(duk_cbor_decode_context *dec_ctx) { + duk_uint32_t res; + + DUK_ASSERT(dec_ctx->off <= dec_ctx->len); + if (DUK_UNLIKELY(dec_ctx->len - dec_ctx->off < 4U)) { + duk__cbor_decode_error(dec_ctx); + } + res = DUK_RAW_READ_U32_BE(dec_ctx->buf + dec_ctx->off); + dec_ctx->off += 4; + return res; +} + +DUK_LOCAL duk_uint8_t duk__cbor_decode_peekbyte(duk_cbor_decode_context *dec_ctx) { + if (DUK_UNLIKELY(dec_ctx->off >= dec_ctx->len)) { + duk__cbor_decode_error(dec_ctx); + } + return dec_ctx->buf[dec_ctx->off]; +} + +DUK_LOCAL void duk__cbor_decode_rewind(duk_cbor_decode_context *dec_ctx, duk_size_t len) { + DUK_ASSERT(len <= dec_ctx->off); /* Caller must ensure. */ + dec_ctx->off -= len; +} + +#if 0 +DUK_LOCAL void duk__cbor_decode_ensure(duk_cbor_decode_context *dec_ctx, duk_size_t len) { + if (dec_ctx->off + len > dec_ctx->len) { + duk__cbor_decode_error(dec_ctx); + } +} +#endif + +DUK_LOCAL const duk_uint8_t *duk__cbor_decode_consume(duk_cbor_decode_context *dec_ctx, duk_size_t len) { + DUK_ASSERT(dec_ctx->off <= dec_ctx->len); + if (DUK_LIKELY(dec_ctx->len - dec_ctx->off >= len)) { + const duk_uint8_t *res = dec_ctx->buf + dec_ctx->off; + dec_ctx->off += len; + return res; + } + + duk__cbor_decode_error(dec_ctx); /* Not enough input. */ + return NULL; +} + +DUK_LOCAL int duk__cbor_decode_checkbreak(duk_cbor_decode_context *dec_ctx) { + if (duk__cbor_decode_peekbyte(dec_ctx) == 0xffU) { + DUK_ASSERT(dec_ctx->off < dec_ctx->len); + dec_ctx->off++; +#if 0 + (void) duk__cbor_decode_readbyte(dec_ctx); +#endif + return 1; + } + return 0; +} + +DUK_LOCAL void duk__cbor_decode_push_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_bool_t negative) { + duk_uint8_t ai; + duk_uint32_t t, t1, t2; +#if 0 + duk_uint64_t t3; +#endif + duk_double_t d1, d2; + duk_double_t d; + + ai = ib & 0x1fU; + if (ai <= 0x17U) { + t = ai; + goto shared_exit; + } + + switch (ai) { + case 0x18U: /* 1 byte */ + t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); + goto shared_exit; + case 0x19U: /* 2 byte */ + t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); + goto shared_exit; + case 0x1aU: /* 4 byte */ + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + goto shared_exit; + case 0x1bU: /* 8 byte */ + /* For uint64 it's important to handle the -1.0 part before + * casting to double: otherwise the adjustment might be lost + * in the cast. Uses: -1.0 - d <=> -(d + 1.0). + */ + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + t2 = t; + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + t1 = t; +#if 0 + t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1; + if (negative) { + if (t3 == DUK_UINT64_MAX) { + /* -(0xffff'ffff'ffff'ffffULL + 1) = + * -0x1'0000'0000'0000'0000 + * + * >>> -0x10000000000000000 + * -18446744073709551616L + */ + return -18446744073709551616.0; + } else { + return -((duk_double_t) (t3 + DUK_U64_CONSTANT(1))); + } + } else { + return (duk_double_t) t3; /* XXX: cast helper */ + } +#endif +#if 0 + t3 = (duk_uint64_t) t2 * DUK_U64_CONSTANT(0x100000000) + (duk_uint64_t) t1; + if (negative) { + /* Simpler version: take advantage of the fact that + * 0xffff'ffff'ffff'ffff and 0x1'0000'0000'0000'0000 + * both round to 0x1'0000'0000'0000'0000: + * > (0xffffffffffffffff).toString(16) + * '10000000000000000' + * > (0x10000000000000000).toString(16) + * '10000000000000000' + * + * For the DUK_UINT64_MAX case we just skip the +1 + * increment to avoid wrapping; the result still + * comes out right for an IEEE double cast. + */ + if (t3 != DUK_UINT64_MAX) { + t3++; + } + return -((duk_double_t) t3); + } else { + return (duk_double_t) t3; /* XXX: cast helper */ + } +#endif +#if 1 + /* Use two double parts, avoids dependency on 64-bit type. + * Avoid precision loss carefully, especially when dealing + * with the required +1 for negative values. + * + * No fastint check for this path at present. + */ + d1 = (duk_double_t) t1; /* XXX: cast helpers */ + d2 = (duk_double_t) t2 * 4294967296.0; + if (negative) { + d1 += 1.0; + } + d = d2 + d1; + if (negative) { + d = -d; + } +#endif + /* XXX: a push and check for fastint API would be nice */ + duk_push_number(dec_ctx->thr, d); + return; + } + + duk__cbor_decode_error(dec_ctx); + return; + + shared_exit: + if (negative) { + /* XXX: a push and check for fastint API would be nice */ + if ((duk_uint_t) t <= (duk_uint_t) -(DUK_INT_MIN + 1)) { + duk_push_int(dec_ctx->thr, -1 - ((duk_int_t) t)); + } else { + duk_push_number(dec_ctx->thr, -1.0 - (duk_double_t) t); + } + } else { + duk_push_uint(dec_ctx->thr, (duk_uint_t) t); + } +} + +DUK_LOCAL void duk__cbor_decode_skip_aival_int(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { + const duk_int8_t skips[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8, -1, -1, -1, -1 + }; + duk_uint8_t ai; + duk_int8_t skip; + + ai = ib & 0x1fU; + skip = skips[ai]; + if (DUK_UNLIKELY(skip < 0)) { + duk__cbor_decode_error(dec_ctx); + } + duk__cbor_decode_consume(dec_ctx, (duk_size_t) skip); + return; +} + +DUK_LOCAL duk_uint32_t duk__cbor_decode_aival_uint32(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib) { + duk_uint8_t ai; + duk_uint32_t t; + + ai = ib & 0x1fU; + if (ai <= 0x17U) { + return (duk_uint32_t) ai; + } + + switch (ai) { + case 0x18U: /* 1 byte */ + t = (duk_uint32_t) duk__cbor_decode_readbyte(dec_ctx); + return t; + case 0x19U: /* 2 byte */ + t = (duk_uint32_t) duk__cbor_decode_read_u16(dec_ctx); + return t; + case 0x1aU: /* 4 byte */ + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + return t; + case 0x1bU: /* 8 byte */ + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + if (t != 0U) { + break; + } + t = (duk_uint32_t) duk__cbor_decode_read_u32(dec_ctx); + return t; + } + + duk__cbor_decode_error(dec_ctx); + return 0U; +} + +DUK_LOCAL void duk__cbor_decode_buffer(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { + duk_uint32_t len; + duk_uint8_t *buf; + const duk_uint8_t *inp; + duk_uint8_t ib; + + ib = duk__cbor_decode_readbyte(dec_ctx); + if ((ib & 0xe0U) != expected_base) { + duk__cbor_decode_error(dec_ctx); + } + /* Indefinite format is rejected by the following on purpose. */ + len = duk__cbor_decode_aival_uint32(dec_ctx, ib); + inp = duk__cbor_decode_consume(dec_ctx, len); + /* XXX: duk_push_fixed_buffer_with_data() would be a nice API addition. */ + buf = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, (duk_size_t) len); + duk_memcpy((void *) buf, (const void *) inp, (size_t) len); +} + +DUK_LOCAL void duk__cbor_decode_join_buffers(duk_cbor_decode_context *dec_ctx, duk_idx_t count) { + duk_size_t total_size = 0; + duk_idx_t top = duk_get_top(dec_ctx->thr); + duk_idx_t base = top - count; /* count is >= 1 */ + duk_idx_t idx; + duk_uint8_t *p = NULL; + + DUK_ASSERT(count >= 1); + DUK_ASSERT(top >= count); + + for (;;) { + /* First round: compute total size. + * Second round: copy into place. + */ + for (idx = base; idx < top; idx++) { + duk_uint8_t *buf_data; + duk_size_t buf_size; + + buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, idx, &buf_size); + if (p != NULL) { + duk_memcpy_unsafe((void *) p, (const void *) buf_data, buf_size); + p += buf_size; + } else { + total_size += buf_size; + if (DUK_UNLIKELY(total_size < buf_size)) { /* Wrap check. */ + duk__cbor_decode_error(dec_ctx); + } + } + } + + if (p != NULL) { + break; + } else { + p = (duk_uint8_t *) duk_push_fixed_buffer(dec_ctx->thr, total_size); + DUK_ASSERT(p != NULL); + } + } + + duk_replace(dec_ctx->thr, base); + duk_pop_n(dec_ctx->thr, count - 1); +} + +DUK_LOCAL void duk__cbor_decode_and_join_strbuf(duk_cbor_decode_context *dec_ctx, duk_uint8_t expected_base) { + duk_idx_t count = 0; + for (;;) { + if (duk__cbor_decode_checkbreak(dec_ctx)) { + break; + } + duk_require_stack(dec_ctx->thr, 1); + duk__cbor_decode_buffer(dec_ctx, expected_base); + count++; + if (DUK_UNLIKELY(count <= 0)) { /* Wrap check. */ + duk__cbor_decode_error(dec_ctx); + } + } + if (count == 0) { + (void) duk_push_fixed_buffer(dec_ctx->thr, 0); + } else if (count > 1) { + duk__cbor_decode_join_buffers(dec_ctx, count); + } +} + +DUK_LOCAL duk_double_t duk__cbor_decode_half_float(duk_cbor_decode_context *dec_ctx) { + duk_double_union u; + const duk_uint8_t *inp; + duk_int_t expt; + duk_uint_t u16; + duk_uint_t tmp; + duk_double_t res; + + inp = duk__cbor_decode_consume(dec_ctx, 2); + u16 = ((duk_uint_t) inp[0] << 8) + (duk_uint_t) inp[1]; + expt = (duk_int_t) ((u16 >> 10) & 0x1fU) - 15; + + /* Reconstruct IEEE double into little endian order first, then convert + * to host order. + */ + + duk_memzero((void *) &u, sizeof(u)); + + if (expt == -15) { + /* Zero or denormal; but note that half float + * denormals become double normals. + */ + if ((u16 & 0x03ffU) == 0) { + u.uc[7] = inp[0] & 0x80U; + } else { + /* Create denormal by first creating a double that + * contains the denormal bits and a leading implicit + * 1-bit. Then subtract away the implicit 1-bit. + * + * 0.mmmmmmmmmm * 2^-14 + * 1.mmmmmmmmmm 0.... * 2^-14 + * -1.0000000000 0.... * 2^-14 + * + * Double exponent: -14 + 1023 = 0x3f1 + */ + u.uc[7] = 0x3fU; + u.uc[6] = 0x10U + (duk_uint8_t) ((u16 >> 6) & 0x0fU); + u.uc[5] = (duk_uint8_t) ((u16 << 2) & 0xffU); /* Mask is really 0xfcU */ + + duk_dblunion_little_to_host(&u); + res = u.d - 0.00006103515625; /* 2^(-14) */ + if (u16 & 0x8000U) { + res = -res; + } + return res; + } + } else if (expt == 16) { + /* +/- Inf or NaN. */ + if ((u16 & 0x03ffU) == 0) { + u.uc[7] = (inp[0] & 0x80U) + 0x7fU; + u.uc[6] = 0xf0U; + } else { + /* Create a 'quiet NaN' with highest + * bit set (there are some platforms + * where the NaN payload convention is + * the opposite). Keep sign. + */ + u.uc[7] = (inp[0] & 0x80U) + 0x7fU; + u.uc[6] = 0xf8U; + } + } else { + /* Normal. */ + tmp = (inp[0] & 0x80U) ? 0x80000000UL : 0UL; + tmp += (duk_uint_t) (expt + 1023) << 20; + tmp += (duk_uint_t) (inp[0] & 0x03U) << 18; + tmp += (duk_uint_t) (inp[1] & 0xffU) << 10; + u.uc[7] = (tmp >> 24) & 0xffU; + u.uc[6] = (tmp >> 16) & 0xffU; + u.uc[5] = (tmp >> 8) & 0xffU; + u.uc[4] = (tmp >> 0) & 0xffU; + } + + duk_dblunion_little_to_host(&u); + return u.d; +} + +DUK_LOCAL void duk__cbor_decode_string(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { + /* If the CBOR string data is not valid UTF-8 it is technically + * invalid CBOR. Possible behaviors at least: + * + * 1. Reject the input, i.e. throw TypeError. + * + * 2. Accept the input, but sanitize non-UTF-8 data into UTF-8 + * using U+FFFD replacements. Also it might make sense to + * decode non-BMP codepoints into surrogates for better + * ECMAScript compatibility. + * + * 3. Accept the input as a Duktape string (which are not always + * valid UTF-8), but reject any input that would create a + * Symbol representation. + * + * Current behavior is 3. + */ + + if (ai == 0x1fU) { + duk_uint8_t *buf_data; + duk_size_t buf_size; + + duk__cbor_decode_and_join_strbuf(dec_ctx, 0x60U); + buf_data = (duk_uint8_t *) duk_require_buffer(dec_ctx->thr, -1, &buf_size); + (void) duk_push_lstring(dec_ctx->thr, (const char *) buf_data, buf_size); + duk_remove(dec_ctx->thr, -2); + } else { + duk_uint32_t len; + const duk_uint8_t *inp; + + len = duk__cbor_decode_aival_uint32(dec_ctx, ib); + inp = duk__cbor_decode_consume(dec_ctx, len); + (void) duk_push_lstring(dec_ctx->thr, (const char *) inp, (duk_size_t) len); + } + if (duk_is_symbol(dec_ctx->thr, -1)) { + /* Refuse to create Symbols when decoding. */ + duk__cbor_decode_error(dec_ctx); + } + + /* XXX: Here a Duktape API call to convert input -> utf-8 with + * replacements would be nice. + */ +} + +DUK_LOCAL duk_bool_t duk__cbor_decode_array(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { + duk_uint32_t idx, len; + + duk__cbor_decode_objarr_entry(dec_ctx); + + /* Support arrays up to 0xfffffffeU in length. 0xffffffff is + * used as an indefinite length marker. + */ + if (ai == 0x1fU) { + len = 0xffffffffUL; + } else { + len = duk__cbor_decode_aival_uint32(dec_ctx, ib); + if (len == 0xffffffffUL) { + goto failure; + } + } + + /* XXX: use bare array? */ + duk_push_array(dec_ctx->thr); + for (idx = 0U; ;) { + if (len == 0xffffffffUL && duk__cbor_decode_checkbreak(dec_ctx)) { + break; + } + if (idx == len) { + if (ai == 0x1fU) { + goto failure; + } + break; + } + duk__cbor_decode_value(dec_ctx); + duk_put_prop_index(dec_ctx->thr, -2, (duk_uarridx_t) idx); + idx++; + if (idx == 0U) { + goto failure; /* wrapped */ + } + } + +#if 0 + success: +#endif + duk__cbor_decode_objarr_exit(dec_ctx); + return 1; + + failure: + /* No need to unwind recursion checks, caller will throw. */ + return 0; +} + +DUK_LOCAL duk_bool_t duk__cbor_decode_map(duk_cbor_decode_context *dec_ctx, duk_uint8_t ib, duk_uint8_t ai) { + duk_uint32_t count; + + duk__cbor_decode_objarr_entry(dec_ctx); + + if (ai == 0x1fU) { + count = 0xffffffffUL; + } else { + count = duk__cbor_decode_aival_uint32(dec_ctx, ib); + if (count == 0xffffffffUL) { + goto failure; + } + } + + /* XXX: use bare object? */ + duk_push_object(dec_ctx->thr); + for (;;) { + if (count == 0xffffffffUL) { + if (duk__cbor_decode_checkbreak(dec_ctx)) { + break; + } + } else { + if (count == 0UL) { + break; + } + count--; + } + + /* Non-string keys are coerced to strings, + * possibly leading to overwriting previous + * keys. Last key of a certain coerced name + * wins. If key is an object, it will coerce + * to '[object Object]' which is consistent + * but potentially misleading. One alternative + * would be to skip non-string keys. + */ + duk__cbor_decode_value(dec_ctx); + duk__cbor_decode_value(dec_ctx); + duk_put_prop(dec_ctx->thr, -3); + } + +#if 0 + success: +#endif + duk__cbor_decode_objarr_exit(dec_ctx); + return 1; + + failure: + /* No need to unwind recursion checks, caller will throw. */ + return 0; +} + +DUK_LOCAL duk_double_t duk__cbor_decode_float(duk_cbor_decode_context *dec_ctx) { + duk_float_union u; + const duk_uint8_t *inp; + inp = duk__cbor_decode_consume(dec_ctx, 4); + duk_memcpy((void *) u.uc, (const void *) inp, 4); + duk_fltunion_big_to_host(&u); + return (duk_double_t) u.f; +} + +DUK_LOCAL duk_double_t duk__cbor_decode_double(duk_cbor_decode_context *dec_ctx) { + duk_double_union u; + const duk_uint8_t *inp; + inp = duk__cbor_decode_consume(dec_ctx, 8); + duk_memcpy((void *) u.uc, (const void *) inp, 8); + duk_dblunion_big_to_host(&u); + return u.d; +} + +#if defined(DUK_CBOR_DECODE_FASTPATH) +#define DUK__CBOR_AI (ib & 0x1fU) + +DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { + duk_uint8_t ib; + + /* Any paths potentially recursing back to duk__cbor_decode_value() + * must perform a Duktape value stack growth check. Avoid the check + * here for simple paths like primitive values. + */ + + reread_initial_byte: + DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len)); + + ib = duk__cbor_decode_readbyte(dec_ctx); + + /* Full initial byte switch, footprint cost over baseline is ~+1kB. */ + /* XXX: Force full switch with no range check. */ + + switch (ib) { + case 0x00U: case 0x01U: case 0x02U: case 0x03U: case 0x04U: case 0x05U: case 0x06U: case 0x07U: + case 0x08U: case 0x09U: case 0x0aU: case 0x0bU: case 0x0cU: case 0x0dU: case 0x0eU: case 0x0fU: + case 0x10U: case 0x11U: case 0x12U: case 0x13U: case 0x14U: case 0x15U: case 0x16U: case 0x17U: + duk_push_uint(dec_ctx->thr, ib); + break; + case 0x18U: case 0x19U: case 0x1aU: case 0x1bU: + duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); + break; + case 0x1cU: case 0x1dU: case 0x1eU: case 0x1fU: + goto format_error; + case 0x20U: case 0x21U: case 0x22U: case 0x23U: case 0x24U: case 0x25U: case 0x26U: case 0x27U: + case 0x28U: case 0x29U: case 0x2aU: case 0x2bU: case 0x2cU: case 0x2dU: case 0x2eU: case 0x2fU: + case 0x30U: case 0x31U: case 0x32U: case 0x33U: case 0x34U: case 0x35U: case 0x36U: case 0x37U: + duk_push_int(dec_ctx->thr, -((duk_int_t) ((ib - 0x20U) + 1U))); + break; + case 0x38U: case 0x39U: case 0x3aU: case 0x3bU: + duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); + break; + case 0x3cU: case 0x3dU: case 0x3eU: case 0x3fU: + goto format_error; + case 0x40U: case 0x41U: case 0x42U: case 0x43U: case 0x44U: case 0x45U: case 0x46U: case 0x47U: + case 0x48U: case 0x49U: case 0x4aU: case 0x4bU: case 0x4cU: case 0x4dU: case 0x4eU: case 0x4fU: + case 0x50U: case 0x51U: case 0x52U: case 0x53U: case 0x54U: case 0x55U: case 0x56U: case 0x57U: + /* XXX: Avoid rewind, we know the length already. */ + DUK_ASSERT(dec_ctx->off > 0U); + dec_ctx->off--; + duk__cbor_decode_buffer(dec_ctx, 0x40U); + break; + case 0x58U: case 0x59U: case 0x5aU: case 0x5bU: + /* XXX: Avoid rewind, decode length inline. */ + DUK_ASSERT(dec_ctx->off > 0U); + dec_ctx->off--; + duk__cbor_decode_buffer(dec_ctx, 0x40U); + break; + case 0x5cU: case 0x5dU: case 0x5eU: + goto format_error; + case 0x5fU: + duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); + break; + case 0x60U: case 0x61U: case 0x62U: case 0x63U: case 0x64U: case 0x65U: case 0x66U: case 0x67U: + case 0x68U: case 0x69U: case 0x6aU: case 0x6bU: case 0x6cU: case 0x6dU: case 0x6eU: case 0x6fU: + case 0x70U: case 0x71U: case 0x72U: case 0x73U: case 0x74U: case 0x75U: case 0x76U: case 0x77U: + /* XXX: Avoid double decode of length. */ + duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); + break; + case 0x78U: case 0x79U: case 0x7aU: case 0x7bU: + /* XXX: Avoid double decode of length. */ + duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); + break; + case 0x7cU: case 0x7dU: case 0x7eU: + goto format_error; + case 0x7fU: + duk__cbor_decode_string(dec_ctx, ib, DUK__CBOR_AI); + break; + case 0x80U: case 0x81U: case 0x82U: case 0x83U: case 0x84U: case 0x85U: case 0x86U: case 0x87U: + case 0x88U: case 0x89U: case 0x8aU: case 0x8bU: case 0x8cU: case 0x8dU: case 0x8eU: case 0x8fU: + case 0x90U: case 0x91U: case 0x92U: case 0x93U: case 0x94U: case 0x95U: case 0x96U: case 0x97U: + if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0x98U: case 0x99U: case 0x9aU: case 0x9bU: + if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0x9cU: case 0x9dU: case 0x9eU: + goto format_error; + case 0x9fU: + if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0xa0U: case 0xa1U: case 0xa2U: case 0xa3U: case 0xa4U: case 0xa5U: case 0xa6U: case 0xa7U: + case 0xa8U: case 0xa9U: case 0xaaU: case 0xabU: case 0xacU: case 0xadU: case 0xaeU: case 0xafU: + case 0xb0U: case 0xb1U: case 0xb2U: case 0xb3U: case 0xb4U: case 0xb5U: case 0xb6U: case 0xb7U: + if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0xb8U: case 0xb9U: case 0xbaU: case 0xbbU: + if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0xbcU: case 0xbdU: case 0xbeU: + goto format_error; + case 0xbfU: + if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, DUK__CBOR_AI) == 0)) { + goto format_error; + } + break; + case 0xc0U: case 0xc1U: case 0xc2U: case 0xc3U: case 0xc4U: case 0xc5U: case 0xc6U: case 0xc7U: + case 0xc8U: case 0xc9U: case 0xcaU: case 0xcbU: case 0xccU: case 0xcdU: case 0xceU: case 0xcfU: + case 0xd0U: case 0xd1U: case 0xd2U: case 0xd3U: case 0xd4U: case 0xd5U: case 0xd6U: case 0xd7U: + /* Tag 0-23: drop. */ + goto reread_initial_byte; + case 0xd8U: case 0xd9U: case 0xdaU: case 0xdbU: + duk__cbor_decode_skip_aival_int(dec_ctx, ib); + goto reread_initial_byte; + case 0xdcU: case 0xddU: case 0xdeU: case 0xdfU: + goto format_error; + case 0xe0U: + goto format_error; + case 0xe1U: + goto format_error; + case 0xe2U: + goto format_error; + case 0xe3U: + goto format_error; + case 0xe4U: + goto format_error; + case 0xe5U: + goto format_error; + case 0xe6U: + goto format_error; + case 0xe7U: + goto format_error; + case 0xe8U: + goto format_error; + case 0xe9U: + goto format_error; + case 0xeaU: + goto format_error; + case 0xebU: + goto format_error; + case 0xecU: + goto format_error; + case 0xedU: + goto format_error; + case 0xeeU: + goto format_error; + case 0xefU: + goto format_error; + case 0xf0U: + goto format_error; + case 0xf1U: + goto format_error; + case 0xf2U: + goto format_error; + case 0xf3U: + goto format_error; + case 0xf4U: + duk_push_false(dec_ctx->thr); + break; + case 0xf5U: + duk_push_true(dec_ctx->thr); + break; + case 0xf6U: + duk_push_null(dec_ctx->thr); + break; + case 0xf7U: + duk_push_undefined(dec_ctx->thr); + break; + case 0xf8U: + /* Simple value 32-255, nothing defined yet, so reject. */ + goto format_error; + case 0xf9U: { + duk_double_t d; + d = duk__cbor_decode_half_float(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0xfaU: { + duk_double_t d; + d = duk__cbor_decode_float(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0xfbU: { + duk_double_t d; + d = duk__cbor_decode_double(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0xfcU: + case 0xfdU: + case 0xfeU: + case 0xffU: + goto format_error; + } /* end switch */ + + return; + + format_error: + duk__cbor_decode_error(dec_ctx); +} +#else /* DUK_CBOR_DECODE_FASTPATH */ +DUK_LOCAL void duk__cbor_decode_value(duk_cbor_decode_context *dec_ctx) { + duk_uint8_t ib, mt, ai; + + /* Any paths potentially recursing back to duk__cbor_decode_value() + * must perform a Duktape value stack growth check. Avoid the check + * here for simple paths like primitive values. + */ + + reread_initial_byte: + DUK_DDD(DUK_DDDPRINT("cbor decode off=%ld len=%ld", (long) dec_ctx->off, (long) dec_ctx->len)); + + ib = duk__cbor_decode_readbyte(dec_ctx); + mt = ib >> 5U; + ai = ib & 0x1fU; + + /* Additional information in [24,27] = [0x18,0x1b] has relatively + * uniform handling for all major types: read 1/2/4/8 additional + * bytes. For major type 7 the 1-byte value is a 'simple type', and + * 2/4/8-byte values are floats. For other major types the 1/2/4/8 + * byte values are integers. The lengths are uniform, but the typing + * is not. + */ + + switch (mt) { + case 0U: { /* unsigned integer */ + duk__cbor_decode_push_aival_int(dec_ctx, ib, 0 /*negative*/); + break; + } + case 1U: { /* negative integer */ + duk__cbor_decode_push_aival_int(dec_ctx, ib, 1 /*negative*/); + break; + } + case 2U: { /* byte string */ + if (ai == 0x1fU) { + duk__cbor_decode_and_join_strbuf(dec_ctx, 0x40U); + } else { + duk__cbor_decode_rewind(dec_ctx, 1U); + duk__cbor_decode_buffer(dec_ctx, 0x40U); + } + break; + } + case 3U: { /* text string */ + duk__cbor_decode_string(dec_ctx, ib, ai); + break; + } + case 4U: { /* array of data items */ + if (DUK_UNLIKELY(duk__cbor_decode_array(dec_ctx, ib, ai) == 0)) { + goto format_error; + } + break; + } + case 5U: { /* map of pairs of data items */ + if (DUK_UNLIKELY(duk__cbor_decode_map(dec_ctx, ib, ai) == 0)) { + goto format_error; + } + break; + } + case 6U: { /* semantic tagging */ + /* Tags are ignored now, re-read initial byte. A tagged + * value may itself be tagged (an unlimited number of times) + * so keep on peeling away tags. + */ + duk__cbor_decode_skip_aival_int(dec_ctx, ib); + goto reread_initial_byte; + } + case 7U: { /* floating point numbers, simple data types, break; other */ + switch (ai) { + case 0x14U: { + duk_push_false(dec_ctx->thr); + break; + } + case 0x15U: { + duk_push_true(dec_ctx->thr); + break; + } + case 0x16U: { + duk_push_null(dec_ctx->thr); + break; + } + case 0x17U: { + duk_push_undefined(dec_ctx->thr); + break; + } + case 0x18U: { /* more simple values (1 byte) */ + /* Simple value encoded in additional byte (none + * are defined so far). RFC 7049 states that the + * follow-up byte must be 32-255 to minimize + * confusion. So, a non-shortest encoding like + * f815 (= true, shortest encoding f5) must be + * rejected. cbor.me tester rejects f815, but + * e.g. Python CBOR binding decodes it as true. + */ + goto format_error; + } + case 0x19U: { /* half-float (2 bytes) */ + duk_double_t d; + d = duk__cbor_decode_half_float(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0x1aU: { /* float (4 bytes) */ + duk_double_t d; + d = duk__cbor_decode_float(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0x1bU: { /* double (8 bytes) */ + duk_double_t d; + d = duk__cbor_decode_double(dec_ctx); + duk_push_number(dec_ctx->thr, d); + break; + } + case 0xffU: /* unexpected break */ + default: { + goto format_error; + } + } /* end switch */ + break; + } + default: { + goto format_error; /* will never actually occur */ + } + } /* end switch */ + + return; + + format_error: + duk__cbor_decode_error(dec_ctx); +} +#endif /* DUK_CBOR_DECODE_FASTPATH */ + +DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { + duk_cbor_encode_context enc_ctx; + duk_uint8_t *buf; + + DUK_UNREF(encode_flags); + + idx = duk_require_normalize_index(thr, idx); + + enc_ctx.thr = thr; + enc_ctx.idx_buf = duk_get_top(thr); + + enc_ctx.len = 64; + buf = (duk_uint8_t *) duk_push_dynamic_buffer(thr, enc_ctx.len); + enc_ctx.ptr = buf; + enc_ctx.buf = buf; + enc_ctx.buf_end = buf + enc_ctx.len; + + enc_ctx.recursion_depth = 0; + enc_ctx.recursion_limit = DUK_USE_CBOR_ENC_RECLIMIT; + + duk_dup(thr, idx); + duk__cbor_encode_req_stack(&enc_ctx); + duk__cbor_encode_value(&enc_ctx); + DUK_ASSERT(enc_ctx.recursion_depth == 0); + duk_resize_buffer(enc_ctx.thr, enc_ctx.idx_buf, (duk_size_t) (enc_ctx.ptr - enc_ctx.buf)); + duk_replace(thr, idx); +} + +DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { + duk_cbor_decode_context dec_ctx; + + DUK_UNREF(decode_flags); + + /* Suppress compile warnings for functions only needed with e.g. + * asserts enabled. + */ + DUK_UNREF(duk__cbor_get_reserve); + + idx = duk_require_normalize_index(thr, idx); + + dec_ctx.thr = thr; + dec_ctx.buf = (const duk_uint8_t *) duk_require_buffer_data(thr, idx, &dec_ctx.len); + dec_ctx.off = 0; + /* dec_ctx.len: set above */ + + dec_ctx.recursion_depth = 0; + dec_ctx.recursion_limit = DUK_USE_CBOR_DEC_RECLIMIT; + + duk__cbor_decode_req_stack(&dec_ctx); + duk__cbor_decode_value(&dec_ctx); + DUK_ASSERT(dec_ctx.recursion_depth == 0); + if (dec_ctx.off != dec_ctx.len) { + (void) duk_type_error(thr, "trailing garbage"); + } + + duk_replace(thr, idx); +} + +#else /* DUK_USE_CBOR_SUPPORT */ + +DUK_LOCAL void duk__cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { + DUK_UNREF(idx); + DUK_UNREF(encode_flags); + DUK_ERROR_UNSUPPORTED(thr); +} + +DUK_LOCAL void duk__cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { + DUK_UNREF(idx); + DUK_UNREF(decode_flags); + DUK_ERROR_UNSUPPORTED(thr); +} + +#endif /* DUK_USE_CBOR_SUPPORT */ + +/* + * Public APIs + */ + +DUK_EXTERNAL void duk_cbor_encode(duk_hthread *thr, duk_idx_t idx, duk_uint_t encode_flags) { + DUK_ASSERT_API_ENTRY(thr); + duk__cbor_encode(thr, idx, encode_flags); +} +DUK_EXTERNAL void duk_cbor_decode(duk_hthread *thr, duk_idx_t idx, duk_uint_t decode_flags) { + DUK_ASSERT_API_ENTRY(thr); + duk__cbor_decode(thr, idx, decode_flags); +} + +#if defined(DUK_USE_CBOR_BUILTIN) +#if defined(DUK_USE_CBOR_SUPPORT) +DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 1); + + duk__cbor_encode(thr, -1, 0 /*flags*/); + + /* Produce an ArrayBuffer by first decoding into a plain buffer which + * mimics a Uint8Array and gettings its .buffer property. + */ + /* XXX: shortcut */ + (void) duk_get_prop_stridx(thr, -1, DUK_STRIDX_LC_BUFFER); + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 1); + + duk__cbor_decode(thr, -1, 0 /*flags*/); + return 1; +} +#else /* DUK_USE_CBOR_SUPPORT */ +DUK_INTERNAL duk_ret_t duk_bi_cbor_encode(duk_hthread *thr) { + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return 0;); +} +DUK_INTERNAL duk_ret_t duk_bi_cbor_decode(duk_hthread *thr) { + DUK_ERROR_UNSUPPORTED(thr); + DUK_WO_NORETURN(return 0;); +} +#endif /* DUK_USE_CBOR_SUPPORT */ +#endif /* DUK_USE_CBOR_BUILTIN */ + +/* automatic undefs */ +#undef DUK__CBOR_AI /* * Date built-ins * @@ -24422,16 +31749,18 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { * */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +/* XXX: currently defines unnecessary symbols when DUK_USE_DATE_BUILTIN is disabled. */ /* * Forward declarations */ -DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk_small_uint_t flags, duk_int_t *out_tzoffset); -DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_context *ctx, duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val); -DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk_double_t *dparts, duk_small_uint_t flags); +DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset); +DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags); +DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val); +DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags); /* * Other file level defines @@ -24472,7 +31801,7 @@ DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk #define DUK__YEAR(x) ((duk_uint8_t) ((x) - 1970)) DUK_LOCAL duk_uint8_t duk__date_equivyear[14] = { #if 1 - /* This is based on V8 EquivalentYear() algorithm (see src/genequivyear.py): + /* This is based on V8 EquivalentYear() algorithm (see util/genequivyear.py): * http://code.google.com/p/v8/source/browse/trunk/src/date.h#146 */ @@ -24499,7 +31828,6 @@ DUK_LOCAL duk_uint8_t duk__date_equivyear[14] = { DUK__YEAR(1976), DUK__YEAR(1988), DUK__YEAR(1972) #endif }; -#undef DUK__YEAR /* * ISO 8601 subset parser. @@ -24596,7 +31924,7 @@ DUK_LOCAL const duk_uint32_t duk__parse_iso8601_control[] = { */ }; -DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const char *str) { +DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_hthread *thr, const char *str) { duk_int_t parts[DUK__NUM_ISO8601_PARSER_PARTS]; duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; duk_double_t d; @@ -24610,7 +31938,7 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch duk_small_uint_t i; /* During parsing, month and day are one-based; set defaults here. */ - DUK_MEMZERO(parts, sizeof(parts)); + duk_memzero(parts, sizeof(parts)); DUK_ASSERT(parts[DUK_DATE_IDX_YEAR] == 0); /* don't care value, year is mandatory */ parts[DUK_DATE_IDX_MONTH] = 1; parts[DUK_DATE_IDX_DAY] = 1; @@ -24636,7 +31964,7 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch DUK_DDD(DUK_DDDPRINT("too many digits -> reject")); goto reject; } - if (part_idx == DUK__PI_MILLISECOND /*msec*/ && ndigits >= 3) { + if (part_idx == DUK__PI_MILLISECOND && ndigits >= 3) { /* ignore millisecond fractions after 3 */ } else { accum = accum * 10 + ((duk_int_t) ch) - ((duk_int_t) DUK_ASC_0) + 0x00; @@ -24644,7 +31972,7 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch } } else { duk_uint_fast32_t match_val; - duk_small_int_t sep_idx; + duk_small_uint_t sep_idx; if (ndigits <= 0) { goto reject; @@ -24768,7 +32096,7 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch } d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - duk_push_number(ctx, d); + duk_push_number(thr, d); return 1; } @@ -24791,7 +32119,7 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch * UTC and '2012/01/01' as local time. */ -DUK_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) { +DUK_LOCAL duk_ret_t duk__parse_string(duk_hthread *thr, const char *str) { /* XXX: there is a small risk here: because the ISO 8601 parser is * very loose, it may end up parsing some datetime values which * would be better parsed with a platform specific parser. @@ -24800,7 +32128,7 @@ DUK_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) { DUK_ASSERT(str != NULL); DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str)); - if (duk__parse_string_iso8601_subset(ctx, str) != 0) { + if (duk__parse_string_iso8601_subset(thr, str) != 0) { return 1; } @@ -24810,14 +32138,14 @@ DUK_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) { * - Don't push anything on stack and return 0 */ - if (DUK_USE_DATE_PARSE_STRING(ctx, str) != 0) { + if (DUK_USE_DATE_PARSE_STRING(thr, str) != 0) { return 1; } #else /* No platform-specific parsing, this is not an error. */ #endif - duk_push_nan(ctx); + duk_push_nan(thr); return 1; } @@ -24837,13 +32165,13 @@ DUK_LOCAL duk_uint8_t duk__days_in_month[12] = { }; /* Maximum iteration count for computing UTC-to-local time offset when - * creating an Ecmascript time value from local parts. + * creating an ECMAScript time value from local parts. */ #define DUK__LOCAL_TZOFFSET_MAXITER 4 /* Because 'day since epoch' can be negative and is used to compute weekday * using a modulo operation, add this multiple of 7 to avoid negative values - * when year is below 1970 epoch. Ecmascript time values are restricted to + * when year is below 1970 epoch. ECMAScript time values are restricted to * +/- 100 million days from epoch, so this adder fits nicely into 32 bits. * Round to a multiple of 7 (= floor(100000000 / 7) * 7) and add margin. */ @@ -25010,9 +32338,9 @@ DUK_LOCAL duk_double_t duk__make_day(duk_double_t year, duk_double_t month, duk_ return (duk_double_t) day_num + day; } -/* Split time value into parts. The time value is assumed to be an internal - * one, i.e. finite, no fractions. Possible local time adjustment has already - * been applied when reading the time value. +/* Split time value into parts. The time value may contain fractions (it may + * come from duk_time_to_components() API call) which are truncated. Possible + * local time adjustment has already been applied when reading the time value. */ DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags) { duk_double_t d1, d2; @@ -25031,17 +32359,18 @@ DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_small_int_t arridx; DUK_ASSERT(DUK_ISFINITE(d)); /* caller checks */ - DUK_ASSERT(DUK_FLOOR(d) == d); /* no fractions in internal time */ + d = DUK_FLOOR(d); /* remove fractions if present */ + DUK_ASSERT(duk_double_equals(DUK_FLOOR(d), d)); - /* The timevalue must be in valid Ecmascript range, but since a local + /* The timevalue must be in valid ECMAScript range, but since a local * time offset can be applied, we need to allow a +/- 24h leeway to * the value. In other words, although the UTC time is within the - * Ecmascript range, the local part values can be just outside of it. + * ECMAScript range, the local part values can be just outside of it. */ DUK_UNREF(duk_bi_date_timeval_in_leeway_range); DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d)); - /* these computations are guaranteed to be exact for the valid + /* These computations are guaranteed to be exact for the valid * E5 time value range, assuming milliseconds without fractions. */ d1 = (duk_double_t) DUK_FMOD(d, (double) DUK_DATE_MSEC_DAY); @@ -25050,13 +32379,13 @@ DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, d1 += (duk_double_t) DUK_DATE_MSEC_DAY; } d2 = DUK_FLOOR((double) (d / (duk_double_t) DUK_DATE_MSEC_DAY)); - DUK_ASSERT(d2 * ((duk_double_t) DUK_DATE_MSEC_DAY) + d1 == d); + DUK_ASSERT(duk_double_equals(d2 * ((duk_double_t) DUK_DATE_MSEC_DAY) + d1, d)); /* now expected to fit into a 32-bit integer */ t1 = (duk_int_t) d1; t2 = (duk_int_t) d2; day_since_epoch = t2; - DUK_ASSERT((duk_double_t) t1 == d1); - DUK_ASSERT((duk_double_t) t2 == d2); + DUK_ASSERT(duk_double_equals((duk_double_t) t1, d1)); + DUK_ASSERT(duk_double_equals((duk_double_t) t2, d2)); /* t1 = milliseconds within day (fits 32 bit) * t2 = day number from epoch (fits 32 bit, may be negative) @@ -25079,7 +32408,7 @@ DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, (long) parts[DUK_DATE_IDX_MILLISECOND])); /* This assert depends on the input parts representing time inside - * the Ecmascript range. + * the ECMAScript range. */ DUK_ASSERT(t2 + DUK__WEEKDAY_MOD_ADDER >= 0); parts[DUK_DATE_IDX_WEEKDAY] = (t2 + 4 + DUK__WEEKDAY_MOD_ADDER) % 7; /* E5.1 Section 15.9.1.6 */ @@ -25199,7 +32528,7 @@ DUK_INTERNAL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dpar * computation happens with intermediate results coerced to * double values (instead of using something more accurate). * E.g. E5.1 Section 15.9.1.11 requires use of IEEE 754 - * rules (= Ecmascript '+' and '*' operators). + * rules (= ECMAScript '+' and '*' operators). * * Without 'volatile' even this approach fails on some platform * and compiler combinations. For instance, gcc 4.8.1 on Ubuntu @@ -25297,21 +32626,21 @@ DUK_INTERNAL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dpar * internal time value. At the end, stack is: [ ... this timeval ]. * Returns the time value. Local time adjustment is done if requested. */ -DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk_small_uint_t flags, duk_int_t *out_tzoffset) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset) { duk_hobject *h; duk_double_t d; duk_int_t tzoffset = 0; - duk_push_this(ctx); - h = duk_get_hobject(ctx, -1); /* XXX: getter with class check, useful in built-ins */ + duk_push_this(thr); + h = duk_get_hobject(thr, -1); /* XXX: getter with class check, useful in built-ins */ if (h == NULL || DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_DATE) { DUK_ERROR_TYPE(thr, "expected Date"); + DUK_WO_NORETURN(return 0.0;); } - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); - d = duk_to_number(ctx, -1); - duk_pop(ctx); + duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); + d = duk_to_number_m1(thr); + duk_pop(thr); if (DUK_ISNAN(d)) { if (flags & DUK_DATE_FLAG_NAN_TO_ZERO) { @@ -25319,6 +32648,7 @@ DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk } if (flags & DUK_DATE_FLAG_NAN_TO_RANGE_ERROR) { DUK_ERROR_RANGE(thr, "Invalid Date"); + DUK_WO_NORETURN(return 0.0;); } } /* if no NaN handling flag, may still be NaN here, but not Inf */ @@ -25339,25 +32669,29 @@ DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk return d; } -DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_context *ctx, duk_small_uint_t flags) { - return duk__push_this_get_timeval_tzoffset(ctx, flags, NULL); +DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags) { + return duk__push_this_get_timeval_tzoffset(thr, flags, NULL); } /* Set timeval to 'this' from dparts, push the new time value onto the * value stack and return 1 (caller can then tail call us). Expects * the value stack to contain 'this' on the stack top. */ -DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk_double_t *dparts, duk_small_uint_t flags) { +DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags) { duk_double_t d; /* [ ... this ] */ d = duk_bi_date_get_timeval_from_dparts(dparts, flags); - duk_push_number(ctx, d); /* -> [ ... this timeval_new ] */ - duk_dup_top(ctx); /* -> [ ... this timeval_new timeval_new ] */ - duk_put_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE); + duk_push_number(thr, d); /* -> [ ... this timeval_new ] */ + duk_dup_top(thr); /* -> [ ... this timeval_new timeval_new ] */ - /* stack top: new time value, return 1 to allow tail calls */ + /* Must force write because e.g. .setYear() must work even when + * the Date instance is frozen. + */ + duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); + + /* Stack top: new time value, return 1 to allow tail calls. */ return 1; } @@ -25385,13 +32719,23 @@ DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, d /* tzoffset seconds are dropped; 16 bits suffice for * time offset in minutes */ + const char *fmt; + duk_small_int_t tmp, arg_hours, arg_minutes; + if (tzoffset >= 0) { - duk_small_int_t tmp = tzoffset / 60; - DUK_SNPRINTF(tzstr, sizeof(tzstr), "+%02d:%02d", (int) (tmp / 60), (int) (tmp % 60)); + tmp = tzoffset; + fmt = "+%02d:%02d"; } else { - duk_small_int_t tmp = -tzoffset / 60; - DUK_SNPRINTF(tzstr, sizeof(tzstr), "-%02d:%02d", (int) (tmp / 60), (int) (tmp % 60)); + tmp = -tzoffset; + fmt = "-%02d:%02d"; } + tmp = tmp / 60; + arg_hours = tmp / 60; + arg_minutes = tmp % 60; + DUK_ASSERT(arg_hours <= 24); /* Even less is actually guaranteed for a valid tzoffset. */ + arg_hours = arg_hours & 0x3f; /* For [0,24] this is a no-op, but fixes GCC 7 warning, see https://github.com/svaarala/duktape/issues/1602. */ + + DUK_SNPRINTF(tzstr, sizeof(tzstr), fmt, (int) arg_hours, (int) arg_minutes); tzstr[sizeof(tzstr) - 1] = (char) 0; } else { tzstr[0] = DUK_ASC_UC_Z; @@ -25422,7 +32766,7 @@ DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, d * internal time value, and format date and/or time in a few formats. * Return value allows tail calls. */ -DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t flags) { +DUK_LOCAL duk_ret_t duk__to_string_helper(duk_hthread *thr, duk_small_uint_t flags) { duk_double_t d; duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; duk_int_t tzoffset; /* seconds, doesn't fit into 16 bits */ @@ -25431,9 +32775,9 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla DUK_UNREF(rc); /* unreferenced with some options */ - d = duk__push_this_get_timeval_tzoffset(ctx, flags, &tzoffset); + d = duk__push_this_get_timeval_tzoffset(thr, flags, &tzoffset); if (DUK_ISNAN(d)) { - duk_push_hstring_stridx(ctx, DUK_STRIDX_INVALID_DATE); + duk_push_hstring_stridx(thr, DUK_STRIDX_INVALID_DATE); return 1; } DUK_ASSERT(DUK_ISFINITE(d)); @@ -25454,7 +32798,7 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * - Don't push anything and return 0 */ - rc = DUK_USE_DATE_FORMAT_STRING(ctx, parts, tzoffset, flags); + rc = DUK_USE_DATE_FORMAT_STRING(thr, parts, tzoffset, flags); if (rc != 0) { return 1; } @@ -25469,7 +32813,7 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * is shared. */ duk__format_parts_iso8601(parts, tzoffset, flags, buf); - duk_push_string(ctx, (const char *) buf); + duk_push_string(thr, (const char *) buf); return 1; } @@ -25478,7 +32822,7 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * local time), push a specified component as a return value to the * value stack and return 1 (caller can then tail call us). */ -DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flags_and_idx) { +DUK_LOCAL duk_ret_t duk__get_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_idx) { duk_double_t d; duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; duk_small_uint_t idx_part = (duk_small_uint_t) (flags_and_idx >> DUK_DATE_FLAG_VALUE_SHIFT); /* unpack args */ @@ -25486,9 +32830,9 @@ DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag DUK_ASSERT_DISABLE(idx_part >= 0); /* unsigned */ DUK_ASSERT(idx_part < DUK_DATE_IDX_NUM_PARTS); - d = duk__push_this_get_timeval(ctx, flags_and_idx); + d = duk__push_this_get_timeval(thr, flags_and_idx); if (DUK_ISNAN(d)) { - duk_push_nan(ctx); + duk_push_nan(thr); return 1; } DUK_ASSERT(DUK_ISFINITE(d)); @@ -25499,7 +32843,7 @@ DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag * only in certain cases. The legacy getYear() getter applies -1900 * unconditionally. */ - duk_push_int(ctx, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]); + duk_push_int(thr, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]); return 1; } @@ -25510,7 +32854,7 @@ DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag * new time value as a return value to the value stack and return 1 * (caller can then tail call us). */ -DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flags_and_maxnargs) { +DUK_LOCAL duk_ret_t duk__set_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_maxnargs) { duk_double_t d; duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; @@ -25519,8 +32863,8 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag duk_small_uint_t idx_first, idx; duk_small_uint_t i; - nargs = duk_get_top(ctx); - d = duk__push_this_get_timeval(ctx, flags_and_maxnargs); + nargs = duk_get_top(thr); + d = duk__push_this_get_timeval(thr, flags_and_maxnargs); DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); if (DUK_ISFINITE(d)) { @@ -25575,10 +32919,10 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag DUK_ASSERT(idx < DUK_DATE_IDX_NUM_PARTS); if (idx == DUK_DATE_IDX_YEAR && (flags_and_maxnargs & DUK_DATE_FLAG_YEAR_FIXUP)) { - duk__twodigit_year_fixup(ctx, (duk_idx_t) i); + duk__twodigit_year_fixup(thr, (duk_idx_t) i); } - dparts[idx] = duk_to_number(ctx, i); + dparts[idx] = duk_to_number(thr, (duk_idx_t) i); if (idx == DUK_DATE_IDX_DAY) { /* Day-of-month is one-based in the API, but zero-based @@ -25598,10 +32942,10 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag * for part setters. */ if (DUK_ISFINITE(d)) { - return duk__set_this_timeval_from_dparts(ctx, dparts, flags_and_maxnargs); + return duk__set_this_timeval_from_dparts(thr, dparts, flags_and_maxnargs); } else { /* Internal timevalue is already NaN, so don't touch it. */ - duk_push_nan(ctx); + duk_push_nan(thr); return 1; } } @@ -25609,7 +32953,7 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag /* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add * 1900 and replace value at idx_val. */ -DUK_LOCAL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val) { +DUK_LOCAL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val) { duk_double_t d; /* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t @@ -25617,25 +32961,25 @@ DUK_LOCAL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val) { */ /* E5 Sections 15.9.3.1, B.2.4, B.2.5 */ - duk_to_number(ctx, idx_val); - if (duk_is_nan(ctx, idx_val)) { + duk_to_number(thr, idx_val); + if (duk_is_nan(thr, idx_val)) { return; } - duk_dup(ctx, idx_val); - duk_to_int(ctx, -1); - d = duk_get_number(ctx, -1); /* get as double to handle huge numbers correctly */ + duk_dup(thr, idx_val); + duk_to_int(thr, -1); + d = duk_get_number(thr, -1); /* get as double to handle huge numbers correctly */ if (d >= 0.0 && d <= 99.0) { d += 1900.0; - duk_push_number(ctx, d); - duk_replace(ctx, idx_val); + duk_push_number(thr, d); + duk_replace(thr, idx_val); } - duk_pop(ctx); + duk_pop(thr); } /* Set datetime parts from stack arguments, defaulting any missing values. * Day-of-week is not set; it is not required when setting the time value. */ -DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, duk_idx_t nargs) { +DUK_LOCAL void duk__set_parts_from_args(duk_hthread *thr, duk_double_t *dparts, duk_idx_t nargs) { duk_double_t d; duk_small_uint_t i; duk_small_uint_t idx; @@ -25643,7 +32987,7 @@ DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, /* Causes a ToNumber() coercion, but doesn't break coercion order since * year is coerced first anyway. */ - duk__twodigit_year_fixup(ctx, 0); + duk__twodigit_year_fixup(thr, 0); /* There are at most 7 args, but we use 8 here so that also * DUK_DATE_IDX_WEEKDAY gets initialized (to zero) to avoid the potential @@ -25653,7 +32997,7 @@ DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, /* Note: rely on index ordering */ idx = DUK_DATE_IDX_YEAR + i; if ((duk_idx_t) i < nargs) { - d = duk_to_number(ctx, (duk_idx_t) i); + d = duk_to_number(thr, (duk_idx_t) i); if (idx == DUK_DATE_IDX_DAY) { /* Convert day from one-based to zero-based (internal). This may * cause the day part to be negative, which is OK. @@ -25677,27 +33021,6 @@ DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, (double) dparts[6], (double) dparts[7])); } -/* - * Helper to format a time value into caller buffer, used by logging. - * 'out_buf' must be at least DUK_BI_DATE_ISO8601_BUFSIZE long. - */ - -DUK_INTERNAL void duk_bi_date_format_timeval(duk_double_t timeval, duk_uint8_t *out_buf) { - duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; - - duk_bi_date_timeval_to_parts(timeval, - parts, - NULL, - DUK_DATE_FLAG_ONEBASED); - - duk__format_parts_iso8601(parts, - 0 /*tzoffset*/, - DUK_DATE_FLAG_TOSTRING_DATE | - DUK_DATE_FLAG_TOSTRING_TIME | - DUK_DATE_FLAG_SEP_T /*flags*/, - out_buf); -} - /* * Indirect magic value lookup for Date methods. * @@ -25831,69 +33154,73 @@ static duk_uint16_t duk__date_magics[] = { DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_YEAR_FIXUP + (3 << DUK_DATE_FLAG_VALUE_SHIFT), }; -DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_context *ctx) { - duk_small_int_t magicidx = (duk_small_uint_t) duk_get_current_magic(ctx); - DUK_ASSERT(magicidx >= 0 && magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t))); +DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_hthread *thr) { + duk_small_uint_t magicidx = (duk_small_uint_t) duk_get_current_magic(thr); + DUK_ASSERT(magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t))); return (duk_small_uint_t) duk__date_magics[magicidx]; } +#if defined(DUK_USE_DATE_BUILTIN) /* * Constructor calls */ -DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_context *ctx) { - duk_idx_t nargs = duk_get_top(ctx); - duk_bool_t is_cons = duk_is_constructor_call(ctx); +DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_hthread *thr) { + duk_idx_t nargs = duk_get_top(thr); + duk_bool_t is_cons = duk_is_constructor_call(thr); duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; duk_double_t d; DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs, (long) is_cons)); - duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE), - DUK_BIDX_DATE_PROTOTYPE); + (void) duk_push_object_helper(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE), + DUK_BIDX_DATE_PROTOTYPE); /* Unlike most built-ins, the internal [[PrimitiveValue]] of a Date * is mutable. */ if (nargs == 0 || !is_cons) { - d = duk__timeclip(DUK_USE_DATE_GET_NOW(ctx)); - duk_push_number(ctx, d); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); + d = duk__timeclip(duk_time_get_ecmascript_time_nofrac(thr)); + duk_push_number(thr, d); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); if (!is_cons) { /* called as a normal function: return new Date().toString() */ - duk_to_string(ctx, -1); + duk_to_string(thr, -1); } return 1; } else if (nargs == 1) { - duk_to_primitive(ctx, 0, DUK_HINT_NONE); - if (duk_is_string(ctx, 0)) { - duk__parse_string(ctx, duk_to_string(ctx, 0)); - duk_replace(ctx, 0); /* may be NaN */ + const char *str; + duk_to_primitive(thr, 0, DUK_HINT_NONE); + str = duk_get_string_notsymbol(thr, 0); + if (str) { + duk__parse_string(thr, str); + duk_replace(thr, 0); /* may be NaN */ } - d = duk__timeclip(duk_to_number(ctx, 0)); - duk_push_number(ctx, d); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); + d = duk__timeclip(duk_to_number(thr, 0)); /* symbols fail here */ + duk_push_number(thr, d); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); return 1; } - duk__set_parts_from_args(ctx, dparts, nargs); + duk__set_parts_from_args(thr, dparts, nargs); /* Parts are in local time, convert when setting. */ - (void) duk__set_this_timeval_from_dparts(ctx, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/); /* -> [ ... this timeval ] */ - duk_pop(ctx); /* -> [ ... this ] */ + (void) duk__set_this_timeval_from_dparts(thr, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/); /* -> [ ... this timeval ] */ + duk_pop(thr); /* -> [ ... this ] */ return 1; } -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_context *ctx) { - return duk__parse_string(ctx, duk_to_string(ctx, 0)); +DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_hthread *thr) { + return duk__parse_string(thr, duk_to_string(thr, 0)); } -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx) { - duk_idx_t nargs = duk_get_top(ctx); +DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_hthread *thr) { + duk_idx_t nargs = duk_get_top(thr); duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; duk_double_t d; @@ -25902,21 +33229,21 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx) { */ if (nargs < 2) { - duk_push_nan(ctx); + duk_push_nan(thr); } else { - duk__set_parts_from_args(ctx, dparts, nargs); + duk__set_parts_from_args(thr, dparts, nargs); d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - duk_push_number(ctx, d); + duk_push_number(thr, d); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_hthread *thr) { duk_double_t d; - d = DUK_USE_DATE_GET_NOW(ctx); - DUK_ASSERT(duk__timeclip(d) == d); /* TimeClip() should never be necessary */ - duk_push_number(ctx, d); + d = duk_time_get_ecmascript_time_nofrac(thr); + DUK_ASSERT(duk_double_equals(duk__timeclip(d), d)); /* TimeClip() should never be necessary */ + duk_push_number(thr, d); return 1; } @@ -25942,7 +33269,7 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) { * Notes: * * - Date.prototype.toGMTString() and Date.prototype.toUTCString() are - * required to be the same Ecmascript function object (!), so it is + * required to be the same ECMAScript function object (!), so it is * omitted from here. * * - Date.prototype.toUTCString(): E5.1 specification does not require a @@ -25954,44 +33281,44 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) { * toISOString() requires a RangeError for invalid date values. */ -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_context *ctx) { - duk_small_uint_t flags = duk__date_get_indirect_magic(ctx); - return duk__to_string_helper(ctx, flags); +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_hthread *thr) { + duk_small_uint_t flags = duk__date_get_indirect_magic(thr); + return duk__to_string_helper(thr, flags); } -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_hthread *thr) { /* This native function is also used for Date.prototype.getTime() * as their behavior is identical. */ - duk_double_t d = duk__push_this_get_timeval(ctx, 0 /*flags*/); /* -> [ this ] */ + duk_double_t d = duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ this ] */ DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); - duk_push_number(ctx, d); + duk_push_number(thr, d); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_hthread *thr) { /* Note: toJSON() is a generic function which works even if 'this' * is not a Date. The sole argument is ignored. */ - duk_push_this(ctx); - duk_to_object(ctx, -1); + duk_push_this(thr); + duk_to_object(thr, -1); - duk_dup_top(ctx); - duk_to_primitive(ctx, -1, DUK_HINT_NUMBER); - if (duk_is_number(ctx, -1)) { - duk_double_t d = duk_get_number(ctx, -1); + duk_dup_top(thr); + duk_to_primitive(thr, -1, DUK_HINT_NUMBER); + if (duk_is_number(thr, -1)) { + duk_double_t d = duk_get_number(thr, -1); if (!DUK_ISFINITE(d)) { - duk_push_null(ctx); + duk_push_null(thr); return 1; } } - duk_pop(ctx); + duk_pop(thr); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_ISO_STRING); - duk_dup(ctx, -2); /* -> [ O toIsoString O ] */ - duk_call_method(ctx, 0); + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_ISO_STRING); + duk_dup_m2(thr); /* -> [ O toIsoString O ] */ + duk_call_method(thr, 0); return 1; } @@ -26036,12 +33363,12 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) { * function (duk_bi_date_prototype_value_of). */ -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx) { - duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(ctx); - return duk__get_part_helper(ctx, flags_and_idx); +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_hthread *thr) { + duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(thr); + return duk__get_part_helper(thr, flags_and_idx); } -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_hthread *thr) { /* * Return (t - LocalTime(t)) in minutes: * @@ -26060,14 +33387,14 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ct duk_int_t tzoffset; /* Note: DST adjustment is determined using UTC time. */ - d = duk__push_this_get_timeval(ctx, 0 /*flags*/); + d = duk__push_this_get_timeval(thr, 0 /*flags*/); DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d)); if (DUK_ISNAN(d)) { - duk_push_nan(ctx); + duk_push_nan(thr); } else { DUK_ASSERT(DUK_ISFINITE(d)); tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d); - duk_push_int(ctx, -tzoffset / 60); + duk_push_int(thr, -tzoffset / 60); } return 1; } @@ -26121,30 +33448,119 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ct * the year will be set regardless of actual argument count. */ -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx) { - duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(ctx); - return duk__set_part_helper(ctx, flags_and_maxnargs); +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_hthread *thr) { + duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(thr); + return duk__set_part_helper(thr, flags_and_maxnargs); } -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_hthread *thr) { duk_double_t d; - (void) duk__push_this_get_timeval(ctx, 0 /*flags*/); /* -> [ timeval this ] */ - d = duk__timeclip(duk_to_number(ctx, 0)); - duk_push_number(ctx, d); - duk_dup_top(ctx); - duk_put_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE); /* -> [ timeval this timeval ] */ + (void) duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ timeval this ] */ + d = duk__timeclip(duk_to_number(thr, 0)); + duk_push_number(thr, d); + duk_dup_top(thr); + /* Must force write because .setTime() must work even when + * the Date instance is frozen. + */ + duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W); + /* -> [ timeval this timeval ] */ return 1; } -#line 1 "duk_bi_date_unix.c" + +/* + * Misc. + */ + +#if defined(DUK_USE_SYMBOL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_date_prototype_toprimitive(duk_hthread *thr) { + duk_size_t hintlen; + const char *hintstr; + duk_int_t hint; + + /* Invokes OrdinaryToPrimitive() with suitable hint. Note that the + * method is generic, and works on non-Date arguments too. + * + * https://www.ecma-international.org/ecma-262/6.0/#sec-date.prototype-@@toprimitive + */ + + duk_push_this(thr); + duk_require_object(thr, -1); + DUK_ASSERT_TOP(thr, 2); + + hintstr = duk_require_lstring(thr, 0, &hintlen); + if ((hintlen == 6 && DUK_STRCMP(hintstr, "string") == 0) || + (hintlen == 7 && DUK_STRCMP(hintstr, "default") == 0)) { + hint = DUK_HINT_STRING; + } else if (hintlen == 6 && DUK_STRCMP(hintstr, "number") == 0) { + hint = DUK_HINT_NUMBER; + } else { + DUK_DCERROR_TYPE_INVALID_ARGS(thr); + } + + duk_to_primitive_ordinary(thr, -1, hint); + return 1; +} +#endif /* DUK_USE_SYMBOL_BUILTIN */ + +#endif /* DUK_USE_DATE_BUILTIN */ + +/* automatic undefs */ +#undef DUK__CF_ACCEPT +#undef DUK__CF_ACCEPT_NUL +#undef DUK__CF_NEG +#undef DUK__DPRINT_DPARTS +#undef DUK__DPRINT_PARTS +#undef DUK__DPRINT_PARTS_AND_DPARTS +#undef DUK__LOCAL_TZOFFSET_MAXITER +#undef DUK__NUM_ISO8601_PARSER_PARTS +#undef DUK__PACK_RULE +#undef DUK__PI_DAY +#undef DUK__PI_HOUR +#undef DUK__PI_MILLISECOND +#undef DUK__PI_MINUTE +#undef DUK__PI_MONTH +#undef DUK__PI_SECOND +#undef DUK__PI_TZHOUR +#undef DUK__PI_TZMINUTE +#undef DUK__PI_YEAR +#undef DUK__PM_DAY +#undef DUK__PM_HOUR +#undef DUK__PM_MILLISECOND +#undef DUK__PM_MINUTE +#undef DUK__PM_MONTH +#undef DUK__PM_SECOND +#undef DUK__PM_TZHOUR +#undef DUK__PM_TZMINUTE +#undef DUK__PM_YEAR +#undef DUK__RULE_MASK_PART_SEP +#undef DUK__SI_COLON +#undef DUK__SI_MINUS +#undef DUK__SI_NUL +#undef DUK__SI_PERIOD +#undef DUK__SI_PLUS +#undef DUK__SI_SPACE +#undef DUK__SI_T +#undef DUK__SI_Z +#undef DUK__SM_COLON +#undef DUK__SM_MINUS +#undef DUK__SM_NUL +#undef DUK__SM_PERIOD +#undef DUK__SM_PLUS +#undef DUK__SM_SPACE +#undef DUK__SM_T +#undef DUK__SM_Z +#undef DUK__UNPACK_RULE +#undef DUK__WEEKDAY_MOD_ADDER +#undef DUK__YEAR /* * Unix-like Date providers * * Generally useful Unix / POSIX / ANSI Date providers. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* The necessary #includes are in place in duk_config.h. */ @@ -26155,19 +33571,19 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx) { #define DUK__STRFTIME_BUF_SIZE 64 #if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY) -/* Get current Ecmascript time (= UNIX/Posix time, but in milliseconds). */ -DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +/* Get current ECMAScript time (= UNIX/Posix time, but in milliseconds). */ +DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(void) { struct timeval tv; duk_double_t d; if (gettimeofday(&tv, NULL) != 0) { - DUK_ERROR_INTERNAL_DEFMSG(thr); + DUK_D(DUK_DPRINT("gettimeofday() failed")); + return 0.0; } + /* As of Duktape 2.2.0 allow fractions. */ d = ((duk_double_t) tv.tv_sec) * 1000.0 + - ((duk_double_t) (tv.tv_usec / 1000)); - DUK_ASSERT(DUK_FLOOR(d) == d); /* no fractions */ + ((duk_double_t) tv.tv_usec) / 1000.0; return d; } @@ -26175,23 +33591,26 @@ DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx) { #if defined(DUK_USE_DATE_NOW_TIME) /* Not a very good provider: only full seconds are available. */ -DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(duk_context *ctx) { +DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(void) { time_t t; - DUK_UNREF(ctx); t = time(NULL); + if (t == (time_t) -1) { + DUK_D(DUK_DPRINT("time() failed")); + return 0.0; + } return ((duk_double_t) t) * 1000.0; } #endif /* DUK_USE_DATE_NOW_TIME */ -#if defined(DUK_USE_DATE_TZO_GMTIME) || defined(DUK_USE_DATE_TZO_GMTIME_R) +#if defined(DUK_USE_DATE_TZO_GMTIME) || defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) /* Get local time offset (in seconds) for a certain (UTC) instant 'd'. */ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { time_t t, t1, t2; duk_int_t parts[DUK_DATE_IDX_NUM_PARTS]; duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS]; struct tm tms[2]; -#ifdef DUK_USE_DATE_TZO_GMTIME +#if defined(DUK_USE_DATE_TZO_GMTIME) struct tm *tm_ptr; #endif @@ -26200,7 +33619,7 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { return 0; } - /* If not within Ecmascript range, some integer time calculations + /* If not within ECMAScript range, some integer time calculations * won't work correctly (and some asserts will fail), so bail out * if so. This fixes test-bug-date-insane-setyear.js. There is * a +/- 24h leeway in this range check to avoid a test262 corner @@ -26251,10 +33670,10 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { * Since we rely on the platform APIs for conversions between local * time and UTC, we can't guarantee the above. Rather, if the platform * has historical DST rules they will be applied. This seems to be the - * general preferred direction in Ecmascript standardization (or at least + * general preferred direction in ECMAScript standardization (or at least * implementations) anyway, and even the equivalent year mapping should * be disabled if the platform is known to handle DST properly for the - * full Ecmascript range. + * full ECMAScript range. * * The following has useful discussion and links: * @@ -26269,16 +33688,19 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { t = (time_t) (d / 1000.0); DUK_DDD(DUK_DDDPRINT("timeval: %lf -> time_t %ld", (double) d, (long) t)); - DUK_MEMZERO((void *) tms, sizeof(struct tm) * 2); + duk_memzero((void *) tms, sizeof(struct tm) * 2); #if defined(DUK_USE_DATE_TZO_GMTIME_R) (void) gmtime_r(&t, &tms[0]); (void) localtime_r(&t, &tms[1]); +#elif defined(DUK_USE_DATE_TZO_GMTIME_S) + (void) gmtime_s(&t, &tms[0]); + (void) localtime_s(&t, &tms[1]); #elif defined(DUK_USE_DATE_TZO_GMTIME) tm_ptr = gmtime(&t); - DUK_MEMCPY((void *) &tms[0], tm_ptr, sizeof(struct tm)); + duk_memcpy((void *) &tms[0], tm_ptr, sizeof(struct tm)); tm_ptr = localtime(&t); - DUK_MEMCPY((void *) &tms[1], tm_ptr, sizeof(struct tm)); + duk_memcpy((void *) &tms[1], tm_ptr, sizeof(struct tm)); #else #error internal error #endif @@ -26308,7 +33730,7 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { * an mktime() error return is the cast above. See e.g.: * http://pubs.opengroup.org/onlinepubs/009695299/functions/mktime.html */ - goto error; + goto mktime_error; } DUK_DDD(DUK_DDDPRINT("t1=%ld (utc), t2=%ld (local)", (long) t1, (long) t2)); @@ -26324,7 +33746,7 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { #endif return (duk_int_t) difftime(t2, t1); - error: + mktime_error: /* XXX: return something more useful, so that caller can throw? */ DUK_D(DUK_DPRINT("mktime() failed, d=%lf", (double) d)); return 0; @@ -26332,20 +33754,20 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { #endif /* DUK_USE_DATE_TZO_GMTIME */ #if defined(DUK_USE_DATE_PRS_STRPTIME) -DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, const char *str) { +DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str) { struct tm tm; time_t t; char buf[DUK__STRPTIME_BUF_SIZE]; - /* copy to buffer with spare to avoid Valgrind gripes from strptime */ + /* Copy to buffer with slack to avoid Valgrind gripes from strptime. */ DUK_ASSERT(str != NULL); - DUK_MEMZERO(buf, sizeof(buf)); /* valgrind whine without this */ + duk_memzero(buf, sizeof(buf)); /* valgrind whine without this */ DUK_SNPRINTF(buf, sizeof(buf), "%s", (const char *) str); buf[sizeof(buf) - 1] = (char) 0; DUK_DDD(DUK_DDDPRINT("parsing: '%s'", (const char *) buf)); - DUK_MEMZERO(&tm, sizeof(tm)); + duk_memzero(&tm, sizeof(tm)); if (strptime((const char *) buf, "%c", &tm) != NULL) { DUK_DDD(DUK_DDDPRINT("before mktime: tm={sec:%ld,min:%ld,hour:%ld,mday:%ld,mon:%ld,year:%ld," "wday:%ld,yday:%ld,isdst:%ld}", @@ -26357,7 +33779,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, cons t = mktime(&tm); DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t)); if (t >= 0) { - duk_push_number(ctx, ((duk_double_t) t) * 1000.0); + duk_push_number(thr, ((duk_double_t) t) * 1000.0); return 1; } } @@ -26367,7 +33789,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, cons #endif /* DUK_USE_DATE_PRS_STRPTIME */ #if defined(DUK_USE_DATE_PRS_GETDATE) -DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const char *str) { +DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str) { struct tm tm; duk_small_int_t rc; time_t t; @@ -26376,7 +33798,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const * convenient for an embeddable interpreter. */ - DUK_MEMZERO(&tm, sizeof(struct tm)); + duk_memzero(&tm, sizeof(struct tm)); rc = (duk_small_int_t) getdate_r(str, &tm); DUK_DDD(DUK_DDDPRINT("getdate_r() -> %ld", (long) rc)); @@ -26384,7 +33806,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const t = mktime(&tm); DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t)); if (t >= 0) { - duk_push_number(ctx, (duk_double_t) t); + duk_push_number(thr, (duk_double_t) t); return 1; } } @@ -26394,31 +33816,31 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const #endif /* DUK_USE_DATE_PRS_GETDATE */ #if defined(DUK_USE_DATE_FMT_STRFTIME) -DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags) { +DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags) { char buf[DUK__STRFTIME_BUF_SIZE]; struct tm tm; const char *fmt; DUK_UNREF(tzoffset); - /* If the platform doesn't support the entire Ecmascript range, we need + /* If the platform doesn't support the entire ECMAScript range, we need * to return 0 so that the caller can fall back to the default formatter. * - * For now, assume that if time_t is 8 bytes or more, the whole Ecmascript + * For now, assume that if time_t is 8 bytes or more, the whole ECMAScript * range is supported. For smaller time_t values (4 bytes in practice), * assumes that the signed 32-bit range is supported. * * XXX: detect this more correctly per platform. The size of time_t is * probably not an accurate guarantee of strftime() supporting or not - * supporting a large time range (the full Ecmascript range). + * supporting a large time range (the full ECMAScript range). */ if (sizeof(time_t) < 8 && - (parts[DUK_DATE_IDX_YEAR] < 1970 || parts[DUK_DATE_IDX_YEAR] > 2037)) { + (parts[DUK_DATE_IDX_YEAR] < 1970 || parts[DUK_DATE_IDX_YEAR] > 2037)) { /* be paranoid for 32-bit time values (even avoiding negative ones) */ return 0; } - DUK_MEMZERO(&tm, sizeof(tm)); + duk_memzero(&tm, sizeof(tm)); tm.tm_sec = parts[DUK_DATE_IDX_SECOND]; tm.tm_min = parts[DUK_DATE_IDX_MINUTE]; tm.tm_hour = parts[DUK_DATE_IDX_HOUR]; @@ -26428,7 +33850,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_ tm.tm_wday = parts[DUK_DATE_IDX_WEEKDAY]; tm.tm_isdst = 0; - DUK_MEMZERO(buf, sizeof(buf)); + duk_memzero(buf, sizeof(buf)); if ((flags & DUK_DATE_FLAG_TOSTRING_DATE) && (flags & DUK_DATE_FLAG_TOSTRING_TIME)) { fmt = "%c"; } else if (flags & DUK_DATE_FLAG_TOSTRING_DATE) { @@ -26440,14 +33862,27 @@ DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_ (void) strftime(buf, sizeof(buf) - 1, fmt, &tm); DUK_ASSERT(buf[sizeof(buf) - 1] == 0); - duk_push_string(ctx, buf); + duk_push_string(thr, buf); return 1; } #endif /* DUK_USE_DATE_FMT_STRFTIME */ -#undef DUK__STRPTIME_BUF_SIZE +#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME) +DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + return (duk_double_t) ts.tv_sec * 1000.0 + (duk_double_t) ts.tv_nsec / 1000000.0; + } else { + DUK_D(DUK_DPRINT("clock_gettime(CLOCK_MONOTONIC) failed")); + return 0.0; + } +} +#endif + +/* automatic undefs */ #undef DUK__STRFTIME_BUF_SIZE -#line 1 "duk_bi_date_windows.c" +#undef DUK__STRPTIME_BUF_SIZE /* * Windows Date providers * @@ -26456,7 +33891,7 @@ DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_ * - http://msdn.microsoft.com/en-us/library/windows/desktop/ms725473(v=vs.85).aspx */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* The necessary #includes are in place in duk_config.h. */ @@ -26472,8 +33907,16 @@ DUK_LOCAL void duk__convert_systime_to_ularge(const SYSTEMTIME *st, ULARGE_INTEG res->HighPart = ft.dwHighDateTime; } } + +#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) +DUK_LOCAL void duk__convert_filetime_to_ularge(const FILETIME *ft, ULARGE_INTEGER *res) { + res->LowPart = ft->dwLowDateTime; + res->HighPart = ft->dwHighDateTime; +} +#endif /* DUK_USE_DATE_NOW_WINDOWS_SUBMS */ + DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) { - DUK_MEMZERO((void *) st, sizeof(*st)); + duk_memzero((void *) st, sizeof(*st)); st->wYear = 1970; st->wMonth = 1; st->wDayOfWeek = 4; /* not sure whether or not needed; Thursday */ @@ -26485,30 +33928,52 @@ DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) { } #endif /* defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS) */ -#ifdef DUK_USE_DATE_NOW_WINDOWS -DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(duk_context *ctx) { +#if defined(DUK_USE_DATE_NOW_WINDOWS) +DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(void) { /* Suggested step-by-step method from documentation of RtlTimeToSecondsSince1970: * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724928(v=vs.85).aspx */ SYSTEMTIME st1, st2; ULARGE_INTEGER tmp1, tmp2; - DUK_UNREF(ctx); - GetSystemTime(&st1); duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); duk__set_systime_jan1970(&st2); duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); - /* Difference is in 100ns units, convert to milliseconds w/o fractions */ - return (duk_double_t) ((tmp1.QuadPart - tmp2.QuadPart) / 10000LL); + /* Difference is in 100ns units, convert to milliseconds, keeping + * fractions since Duktape 2.2.0. This is only theoretical because + * SYSTEMTIME is limited to milliseconds. + */ + return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0; } #endif /* DUK_USE_DATE_NOW_WINDOWS */ +#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) +DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows_subms(void) { + /* Variant of the basic algorithm using GetSystemTimePreciseAsFileTime() + * for more accuracy. + */ + FILETIME ft1; + SYSTEMTIME st2; + ULARGE_INTEGER tmp1, tmp2; + + GetSystemTimePreciseAsFileTime(&ft1); + duk__convert_filetime_to_ularge((const FILETIME *) &ft1, &tmp1); + + duk__set_systime_jan1970(&st2); + duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); + + /* Difference is in 100ns units, convert to milliseconds, keeping + * fractions since Duktape 2.2.0. + */ + return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0; +} +#endif /* DUK_USE_DATE_NOW_WINDOWS */ #if defined(DUK_USE_DATE_TZO_WINDOWS) -DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) { +DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) { SYSTEMTIME st1; SYSTEMTIME st2; SYSTEMTIME st3; @@ -26519,7 +33984,7 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t /* XXX: handling of timestamps outside Windows supported range. * How does Windows deal with dates before 1600? Does windows - * support all Ecmascript years (like -200000 and +200000)? + * support all ECMAScript years (like -200000 and +200000)? * Should equivalent year mapping be used here too? If so, use * a shared helper (currently integrated into timeval-to-parts). */ @@ -26535,7 +34000,10 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t ft1.dwLowDateTime = tmp2.LowPart; ft1.dwHighDateTime = tmp2.HighPart; - FileTimeToSystemTime((const FILETIME *) &ft1, &st2); + if (FileTimeToSystemTime((const FILETIME *) &ft1, &st2) == 0) { + DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0")); + return 0; + } if (SystemTimeToTzSpecificLocalTime((LPTIME_ZONE_INFORMATION) NULL, &st2, &st3) == 0) { DUK_D(DUK_DPRINT("SystemTimeToTzSpecificLocalTime() failed, return tzoffset 0")); return 0; @@ -26543,258 +34011,157 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t duk__convert_systime_to_ularge((const SYSTEMTIME *) &st3, &tmp3); /* Positive if local time ahead of UTC. */ - return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000000LL); /* seconds */ + return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / DUK_I64_CONSTANT(10000000)); /* seconds */ } #endif /* DUK_USE_DATE_TZO_WINDOWS */ -#line 1 "duk_bi_duktape.c" + +#if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) +DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d) { + SYSTEMTIME st1; + SYSTEMTIME st2; + FILETIME ft1; + FILETIME ft2; + ULARGE_INTEGER tmp1; + ULARGE_INTEGER tmp2; + + /* Do a similar computation to duk_bi_date_get_local_tzoffset_windows + * but without accounting for daylight savings time. Use this on + * Windows platforms (like Durango) that don't support the + * SystemTimeToTzSpecificLocalTime() call. + */ + + /* current time not needed for this computation */ + DUK_UNREF(d); + + duk__set_systime_jan1970(&st1); + duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); + + ft1.dwLowDateTime = tmp1.LowPart; + ft1.dwHighDateTime = tmp1.HighPart; + if (FileTimeToLocalFileTime((const FILETIME *) &ft1, &ft2) == 0) { + DUK_D(DUK_DPRINT("FileTimeToLocalFileTime() failed, return tzoffset 0")); + return 0; + } + if (FileTimeToSystemTime((const FILETIME *) &ft2, &st2) == 0) { + DUK_D(DUK_DPRINT("FileTimeToSystemTime() failed, return tzoffset 0")); + return 0; + } + duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2); + + return (duk_int_t) (((LONGLONG) tmp2.QuadPart - (LONGLONG) tmp1.QuadPart) / DUK_I64_CONSTANT(10000000)); /* seconds */ +} +#endif /* DUK_USE_DATE_TZO_WINDOWS_NO_DST */ + +#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) +DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void) { + LARGE_INTEGER count, freq; + + /* There are legacy issues with QueryPerformanceCounter(): + * - Potential jumps: https://support.microsoft.com/en-us/help/274323/performance-counter-value-may-unexpectedly-leap-forward + * - Differences between cores (XP): https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions + * + * We avoid these by enabling QPC by default only for Vista or later. + */ + + if (QueryPerformanceCounter(&count) && QueryPerformanceFrequency(&freq)) { + /* XXX: QueryPerformanceFrequency() can be cached */ + return (duk_double_t) count.QuadPart / (duk_double_t) freq.QuadPart * 1000.0; + } else { + /* MSDN: "On systems that run Windows XP or later, the function + * will always succeed and will thus never return zero." + * Provide minimal error path just in case user enables this + * feature in pre-XP Windows. + */ + return 0.0; + } +} +#endif /* DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC */ /* * Duktape built-ins * * Size optimization note: it might seem that vararg multipurpose functions * like fin(), enc(), and dec() are not very size optimal, but using a single - * user-visible Ecmascript function saves a lot of run-time footprint; each + * user-visible ECMAScript function saves a lot of run-time footprint; each * Function instance takes >100 bytes. Using a shared native helper and a * 'magic' value won't save much if there are multiple Function instances * anyway. */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -/* Raw helper to extract internal information / statistics about a value. - * The return values are version specific and must not expose anything - * that would lead to security issues (e.g. exposing compiled function - * 'data' buffer might be an issue). Currently only counts and sizes and - * such are given so there should not be a security impact. - */ -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_tval *tv; - duk_heaphdr *h; - duk_int_t i, n; +#if defined(DUK_USE_DUKTAPE_BUILTIN) - DUK_UNREF(thr); - - /* result array */ - duk_push_array(ctx); /* -> [ val arr ] */ - - /* type tag (public) */ - duk_push_int(ctx, duk_get_type(ctx, 0)); - - /* address */ - tv = duk_get_tval(ctx, 0); - DUK_ASSERT(tv != NULL); /* because arg count is 1 */ - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - h = DUK_TVAL_GET_HEAPHDR(tv); - duk_push_pointer(ctx, (void *) h); - } else { - /* internal type tag */ - duk_push_int(ctx, (duk_int_t) DUK_TVAL_GET_TAG(tv)); - goto done; - } - DUK_ASSERT(h != NULL); - - /* refcount */ -#ifdef DUK_USE_REFERENCE_COUNTING - duk_push_size_t(ctx, DUK_HEAPHDR_GET_REFCOUNT(h)); -#else - duk_push_undefined(ctx); -#endif - - /* heaphdr size and additional allocation size, followed by - * type specific stuff (with varying value count) - */ - switch ((duk_small_int_t) DUK_HEAPHDR_GET_TYPE(h)) { - case DUK_HTYPE_STRING: { - duk_hstring *h_str = (duk_hstring *) h; - duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hstring) + DUK_HSTRING_GET_BYTELEN(h_str) + 1)); - break; - } - case DUK_HTYPE_OBJECT: { - duk_hobject *h_obj = (duk_hobject *) h; - duk_small_uint_t hdr_size; - if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { - hdr_size = (duk_small_uint_t) sizeof(duk_hcompiledfunction); - } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h_obj)) { - hdr_size = (duk_small_uint_t) sizeof(duk_hnativefunction); - } else if (DUK_HOBJECT_IS_THREAD(h_obj)) { - hdr_size = (duk_small_uint_t) sizeof(duk_hthread); -#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - } else if (DUK_HOBJECT_IS_BUFFEROBJECT(h_obj)) { - hdr_size = (duk_small_uint_t) sizeof(duk_hbufferobject); -#endif - } else { - hdr_size = (duk_small_uint_t) sizeof(duk_hobject); - } - duk_push_uint(ctx, (duk_uint_t) hdr_size); - duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_P_ALLOC_SIZE(h_obj)); - duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ESIZE(h_obj)); - /* Note: e_next indicates the number of gc-reachable entries - * in the entry part, and also indicates the index where the - * next new property would be inserted. It does *not* indicate - * the number of non-NULL keys present in the object. That - * value could be counted separately but requires a pass through - * the key list. - */ - duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ENEXT(h_obj)); - duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_ASIZE(h_obj)); - duk_push_uint(ctx, (duk_uint_t) DUK_HOBJECT_GET_HSIZE(h_obj)); - if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h_obj)) { - duk_hbuffer *h_data = (duk_hbuffer *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, (duk_hcompiledfunction *) h_obj); - if (h_data) { - duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_data)); - } else { - duk_push_uint(ctx, 0); - } - } - break; - } - case DUK_HTYPE_BUFFER: { - duk_hbuffer *h_buf = (duk_hbuffer *) h; - if (DUK_HBUFFER_HAS_DYNAMIC(h_buf)) { - if (DUK_HBUFFER_HAS_EXTERNAL(h_buf)) { - duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_external))); - } else { - /* When alloc_size == 0 the second allocation may not - * actually exist. - */ - duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_dynamic))); - } - duk_push_uint(ctx, (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf))); - } else { - duk_push_uint(ctx, (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf) + 1)); - } - break; - - } - } - - done: - /* set values into ret array */ - /* XXX: primitive to make array from valstack slice */ - n = duk_get_top(ctx); - for (i = 2; i < n; i++) { - duk_dup(ctx, i); - duk_put_prop_index(ctx, 1, i - 2); - } - duk_dup(ctx, 1); +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_hthread *thr) { + duk_inspect_value(thr, -1); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_activation *act; - duk_uint_fast32_t pc; - duk_uint_fast32_t line; +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_hthread *thr) { duk_int_t level; - /* -1 = top callstack entry, callstack[callstack_top - 1] - * -callstack_top = bottom callstack entry, callstack[0] - */ - level = duk_to_int(ctx, 0); - if (level >= 0 || -level > (duk_int_t) thr->callstack_top) { - return 0; - } - DUK_ASSERT(level >= -((duk_int_t) thr->callstack_top) && level <= -1); - act = thr->callstack + thr->callstack_top + level; - - duk_push_object(ctx); - - duk_push_tval(ctx, &act->tv_func); - - /* Relevant PC is just before current one because PC is - * post-incremented. This should match what error augment - * code does. - */ - pc = duk_hthread_get_act_prev_pc(thr, act); - duk_push_uint(ctx, (duk_uint_t) pc); - -#if defined(DUK_USE_PC2LINE) - line = duk_hobject_pc2line_query(ctx, -2, pc); -#else - line = 0; -#endif - duk_push_uint(ctx, (duk_uint_t) line); - - /* Providing access to e.g. act->lex_env would be dangerous: these - * internal structures must never be accessible to the application. - * Duktape relies on them having consistent data, and this consistency - * is only asserted for, not checked for. - */ - - /* [ level obj func pc line ] */ - - /* XXX: version specific array format instead? */ - duk_xdef_prop_stridx_wec(ctx, -4, DUK_STRIDX_LINE_NUMBER); - duk_xdef_prop_stridx_wec(ctx, -3, DUK_STRIDX_PC); - duk_xdef_prop_stridx_wec(ctx, -2, DUK_STRIDX_LC_FUNCTION); + level = duk_to_int(thr, 0); + duk_inspect_callstack_entry(thr, level); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_context *ctx) { -#ifdef DUK_USE_MARK_AND_SWEEP - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_hthread *thr) { duk_small_uint_t flags; - duk_bool_t rc; - flags = (duk_small_uint_t) duk_get_uint(ctx, 0); - rc = duk_heap_mark_and_sweep(thr->heap, flags); + flags = (duk_small_uint_t) duk_get_uint(thr, 0); + duk_heap_mark_and_sweep(thr->heap, flags); /* XXX: Not sure what the best return value would be in the API. - * Return a boolean for now. Note that rc == 0 is success (true). + * Return true for now. */ - duk_push_boolean(ctx, !rc); + duk_push_true(thr); return 1; -#else - DUK_UNREF(ctx); - return 0; -#endif } -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx) { - (void) duk_require_hobject(ctx, 0); - if (duk_get_top(ctx) >= 2) { +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_hthread *thr) { + (void) duk_require_hobject(thr, 0); + if (duk_get_top(thr) >= 2) { /* Set: currently a finalizer is disabled by setting it to * undefined; this does not remove the property at the moment. * The value could be type checked to be either a function * or something else; if something else, the property could - * be deleted. + * be deleted. Must use duk_set_finalizer() to keep + * DUK_HOBJECT_FLAG_HAVE_FINALIZER in sync. */ - duk_set_top(ctx, 2); - (void) duk_put_prop_stridx(ctx, 0, DUK_STRIDX_INT_FINALIZER); + duk_set_top(thr, 2); + duk_set_finalizer(thr, 0); return 0; } else { /* Get. */ - DUK_ASSERT(duk_get_top(ctx) == 1); - duk_get_prop_stridx(ctx, 0, DUK_STRIDX_INT_FINALIZER); + DUK_ASSERT(duk_get_top(thr) == 1); + duk_get_finalizer(thr, 0); return 1; } } +#endif /* DUK_USE_FINALIZER_SUPPORT */ -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_hthread *thr) { duk_hstring *h_str; - DUK_UNREF(thr); - /* Vararg function: must be careful to check/require arguments. * The JSON helpers accept invalid indices and treat them like * non-existent optional parameters. */ - h_str = duk_require_hstring(ctx, 0); - duk_require_valid_index(ctx, 1); + h_str = duk_require_hstring(thr, 0); /* Could reject symbols, but no point: won't match comparisons. */ + duk_require_valid_index(thr, 1); if (h_str == DUK_HTHREAD_STRING_HEX(thr)) { - duk_set_top(ctx, 2); - duk_hex_encode(ctx, 1); - DUK_ASSERT_TOP(ctx, 2); + duk_set_top(thr, 2); + duk_hex_encode(thr, 1); + DUK_ASSERT_TOP(thr, 2); } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) { - duk_set_top(ctx, 2); - duk_base64_encode(ctx, 1); - DUK_ASSERT_TOP(ctx, 2); -#ifdef DUK_USE_JX + duk_set_top(thr, 2); + duk_base64_encode(thr, 1); + DUK_ASSERT_TOP(thr, 2); +#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX) } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) { - duk_bi_json_stringify_helper(ctx, + duk_bi_json_stringify_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, 3 /*idx_space*/, @@ -26802,9 +34169,9 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) { DUK_JSON_FLAG_ASCII_ONLY | DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/); #endif -#ifdef DUK_USE_JC +#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC) } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) { - duk_bi_json_stringify_helper(ctx, + duk_bi_json_stringify_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, 3 /*idx_space*/, @@ -26812,49 +34179,46 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) { DUK_JSON_FLAG_ASCII_ONLY /*flags*/); #endif } else { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_hthread *thr) { duk_hstring *h_str; - DUK_UNREF(thr); - /* Vararg function: must be careful to check/require arguments. * The JSON helpers accept invalid indices and treat them like * non-existent optional parameters. */ - h_str = duk_require_hstring(ctx, 0); - duk_require_valid_index(ctx, 1); + h_str = duk_require_hstring(thr, 0); /* Could reject symbols, but no point: won't match comparisons */ + duk_require_valid_index(thr, 1); if (h_str == DUK_HTHREAD_STRING_HEX(thr)) { - duk_set_top(ctx, 2); - duk_hex_decode(ctx, 1); - DUK_ASSERT_TOP(ctx, 2); + duk_set_top(thr, 2); + duk_hex_decode(thr, 1); + DUK_ASSERT_TOP(thr, 2); } else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) { - duk_set_top(ctx, 2); - duk_base64_decode(ctx, 1); - DUK_ASSERT_TOP(ctx, 2); -#ifdef DUK_USE_JX + duk_set_top(thr, 2); + duk_base64_decode(thr, 1); + DUK_ASSERT_TOP(thr, 2); +#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX) } else if (h_str == DUK_HTHREAD_STRING_JX(thr)) { - duk_bi_json_parse_helper(ctx, + duk_bi_json_parse_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, DUK_JSON_FLAG_EXT_CUSTOM /*flags*/); #endif -#ifdef DUK_USE_JC +#if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC) } else if (h_str == DUK_HTHREAD_STRING_JC(thr)) { - duk_bi_json_parse_helper(ctx, + duk_bi_json_parse_helper(thr, 1 /*idx_value*/, 2 /*idx_replacer*/, DUK_JSON_FLAG_EXT_COMPATIBLE /*flags*/); #endif } else { - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } return 1; } @@ -26863,19 +34227,558 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx) { * Compact an object */ -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_context *ctx) { - DUK_ASSERT_TOP(ctx, 1); - duk_compact(ctx, 0); +DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 1); + duk_compact(thr, 0); return 1; /* return the argument object */ } -#line 1 "duk_bi_error.c" + +#endif /* DUK_USE_DUKTAPE_BUILTIN */ +/* + * WHATWG Encoding API built-ins + * + * API specification: https://encoding.spec.whatwg.org/#api + * Web IDL: https://www.w3.org/TR/WebIDL/ + */ + +/* #include duk_internal.h -> already included */ + +/* + * Data structures for encoding/decoding + */ + +typedef struct { + duk_uint8_t *out; /* where to write next byte(s) */ + duk_codepoint_t lead; /* lead surrogate */ +} duk__encode_context; + +typedef struct { + /* UTF-8 decoding state */ + duk_codepoint_t codepoint; /* built up incrementally */ + duk_uint8_t upper; /* max value of next byte (decode error otherwise) */ + duk_uint8_t lower; /* min value of next byte (ditto) */ + duk_uint8_t needed; /* how many more bytes we need */ + duk_uint8_t bom_handled; /* BOM seen or no longer expected */ + + /* Decoder configuration */ + duk_uint8_t fatal; + duk_uint8_t ignore_bom; +} duk__decode_context; + +/* The signed duk_codepoint_t type is used to signal a decoded codepoint + * (>= 0) or various other states using negative values. + */ +#define DUK__CP_CONTINUE (-1) /* continue to next byte, no completed codepoint */ +#define DUK__CP_ERROR (-2) /* decoding error */ +#define DUK__CP_RETRY (-3) /* decoding error; retry last byte */ + +/* + * Raw helpers for encoding/decoding + */ + +/* Emit UTF-8 (= CESU-8) encoded U+FFFD (replacement char), i.e. ef bf bd. */ +DUK_LOCAL duk_uint8_t *duk__utf8_emit_repl(duk_uint8_t *ptr) { + *ptr++ = 0xef; + *ptr++ = 0xbf; + *ptr++ = 0xbd; + return ptr; +} + +DUK_LOCAL void duk__utf8_decode_init(duk__decode_context *dec_ctx) { + /* (Re)init the decoding state of 'dec_ctx' but leave decoder + * configuration fields untouched. + */ + dec_ctx->codepoint = 0x0000L; + dec_ctx->upper = 0xbf; + dec_ctx->lower = 0x80; + dec_ctx->needed = 0; + dec_ctx->bom_handled = 0; +} + +DUK_LOCAL duk_codepoint_t duk__utf8_decode_next(duk__decode_context *dec_ctx, duk_uint8_t x) { + /* + * UTF-8 algorithm based on the Encoding specification: + * https://encoding.spec.whatwg.org/#utf-8-decoder + * + * Two main states: decoding initial byte vs. decoding continuation + * bytes. Shortest length encoding is validated by restricting the + * allowed range of first continuation byte using 'lower' and 'upper'. + */ + + if (dec_ctx->needed == 0) { + /* process initial byte */ + if (x <= 0x7f) { + /* U+0000-U+007F, 1 byte (ASCII) */ + return (duk_codepoint_t) x; + } else if (x >= 0xc2 && x <= 0xdf) { + /* U+0080-U+07FF, 2 bytes */ + dec_ctx->needed = 1; + dec_ctx->codepoint = x & 0x1f; + DUK_ASSERT(dec_ctx->lower == 0x80); + DUK_ASSERT(dec_ctx->upper == 0xbf); + return DUK__CP_CONTINUE; + } else if (x >= 0xe0 && x <= 0xef) { + /* U+0800-U+FFFF, 3 bytes */ + if (x == 0xe0) { + dec_ctx->lower = 0xa0; + DUK_ASSERT(dec_ctx->upper == 0xbf); + } else if (x == 0xed) { + DUK_ASSERT(dec_ctx->lower == 0x80); + dec_ctx->upper = 0x9f; + } + dec_ctx->needed = 2; + dec_ctx->codepoint = x & 0x0f; + return DUK__CP_CONTINUE; + } else if (x >= 0xf0 && x <= 0xf4) { + /* U+010000-U+10FFFF, 4 bytes */ + if (x == 0xf0) { + dec_ctx->lower = 0x90; + DUK_ASSERT(dec_ctx->upper == 0xbf); + } else if (x == 0xf4) { + DUK_ASSERT(dec_ctx->lower == 0x80); + dec_ctx->upper = 0x8f; + } + dec_ctx->needed = 3; + dec_ctx->codepoint = x & 0x07; + return DUK__CP_CONTINUE; + } else { + /* not a legal initial byte */ + return DUK__CP_ERROR; + } + } else { + /* process continuation byte */ + if (x >= dec_ctx->lower && x <= dec_ctx->upper) { + dec_ctx->lower = 0x80; + dec_ctx->upper = 0xbf; + dec_ctx->codepoint = (dec_ctx->codepoint << 6) | (x & 0x3f); + if (--dec_ctx->needed > 0) { + /* need more bytes */ + return DUK__CP_CONTINUE; + } else { + /* got a codepoint */ + duk_codepoint_t ret; + DUK_ASSERT(dec_ctx->codepoint <= 0x10ffffL); /* Decoding rules guarantee. */ + ret = dec_ctx->codepoint; + dec_ctx->codepoint = 0x0000L; + dec_ctx->needed = 0; + return ret; + } + } else { + /* We just encountered an illegal UTF-8 continuation byte. This might + * be the initial byte of the next character; if we return a plain + * error status and the decoder is in replacement mode, the character + * will be masked. We still need to alert the caller to the error + * though. + */ + dec_ctx->codepoint = 0x0000L; + dec_ctx->needed = 0; + dec_ctx->lower = 0x80; + dec_ctx->upper = 0xbf; + return DUK__CP_RETRY; + } + } +} + +#if defined(DUK_USE_ENCODING_BUILTINS) +DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { + duk__encode_context *enc_ctx; + + DUK_ASSERT(codepoint >= 0); + enc_ctx = (duk__encode_context *) udata; + DUK_ASSERT(enc_ctx != NULL); + +#if !defined(DUK_USE_PREFER_SIZE) + if (codepoint <= 0x7f && enc_ctx->lead == 0x0000L) { + /* Fast path for ASCII. */ + *enc_ctx->out++ = (duk_uint8_t) codepoint; + return; + } +#endif + + if (DUK_UNLIKELY(codepoint > 0x10ffffL)) { + /* cannot legally encode in UTF-8 */ + codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; + } else if (codepoint >= 0xd800L && codepoint <= 0xdfffL) { + if (codepoint <= 0xdbffL) { + /* high surrogate */ + duk_codepoint_t prev_lead = enc_ctx->lead; + enc_ctx->lead = codepoint; + if (prev_lead == 0x0000L) { + /* high surrogate, no output */ + return; + } else { + /* consecutive high surrogates, consider first one unpaired */ + codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; + } + } else { + /* low surrogate */ + if (enc_ctx->lead != 0x0000L) { + codepoint = (duk_codepoint_t) (0x010000L + ((enc_ctx->lead - 0xd800L) << 10) + (codepoint - 0xdc00L)); + enc_ctx->lead = 0x0000L; + } else { + /* unpaired low surrogate */ + DUK_ASSERT(enc_ctx->lead == 0x0000L); + codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; + } + } + } else { + if (enc_ctx->lead != 0x0000L) { + /* unpaired high surrogate: emit replacement character and the input codepoint */ + enc_ctx->lead = 0x0000L; + enc_ctx->out = duk__utf8_emit_repl(enc_ctx->out); + } + } + + /* Codepoint may be original input, a decoded surrogate pair, or may + * have been replaced with U+FFFD. + */ + enc_ctx->out += duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, enc_ctx->out); +} +#endif /* DUK_USE_ENCODING_BUILTINS */ + +/* Shared helper for buffer-to-string using a TextDecoder() compatible UTF-8 + * decoder. + */ +DUK_LOCAL duk_ret_t duk__decode_helper(duk_hthread *thr, duk__decode_context *dec_ctx) { + const duk_uint8_t *input; + duk_size_t len = 0; + duk_size_t len_tmp; + duk_bool_t stream = 0; + duk_codepoint_t codepoint; + duk_uint8_t *output; + const duk_uint8_t *in; + duk_uint8_t *out; + + DUK_ASSERT(dec_ctx != NULL); + + /* Careful with input buffer pointer: any side effects involving + * code execution (e.g. getters, coercion calls, and finalizers) + * may cause a resize and invalidate a pointer we've read. This + * is why the pointer is actually looked up at the last minute. + * Argument validation must still happen first to match WHATWG + * required side effect order. + */ + + if (duk_is_undefined(thr, 0)) { + duk_push_fixed_buffer_nozero(thr, 0); + duk_replace(thr, 0); + } + (void) duk_require_buffer_data(thr, 0, &len); /* Need 'len', avoid pointer. */ + + if (duk_check_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED | + DUK_TYPE_MASK_NULL | + DUK_TYPE_MASK_NONE)) { + /* Use defaults, treat missing value like undefined. */ + } else { + duk_require_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED | + DUK_TYPE_MASK_NULL | + DUK_TYPE_MASK_LIGHTFUNC | + DUK_TYPE_MASK_BUFFER | + DUK_TYPE_MASK_OBJECT); + if (duk_get_prop_literal(thr, 1, "stream")) { + stream = duk_to_boolean(thr, -1); + } + } + + /* Allowance is 3*len in the general case because all bytes may potentially + * become U+FFFD. If the first byte completes a non-BMP codepoint it will + * decode to a CESU-8 surrogate pair (6 bytes) so we allow 3 extra bytes to + * compensate: (1*3)+3 = 6. Non-BMP codepoints are safe otherwise because + * the 4->6 expansion is well under the 3x allowance. + * + * XXX: As with TextEncoder, need a better buffer allocation strategy here. + */ + if (len >= (DUK_HBUFFER_MAX_BYTELEN / 3) - 3) { + DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG); + DUK_WO_NORETURN(return 0;); + } + output = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, 3 + (3 * len)); /* used parts will be always manually written over */ + + input = (const duk_uint8_t *) duk_get_buffer_data(thr, 0, &len_tmp); + DUK_ASSERT(input != NULL || len == 0); + if (DUK_UNLIKELY(len != len_tmp)) { + /* Very unlikely but possible: source buffer was resized by + * a side effect when fixed buffer was pushed. Output buffer + * may not be large enough to hold output, so just fail if + * length has changed. + */ + DUK_D(DUK_DPRINT("input buffer resized by side effect, fail")); + goto fail_type; + } + + /* From this point onwards it's critical that no side effect occur + * which may disturb 'input': finalizer execution, property accesses, + * active coercions, etc. Even an allocation related mark-and-sweep + * may affect the pointer because it may trigger a pending finalizer. + */ + + in = input; + out = output; + while (in < input + len) { + codepoint = duk__utf8_decode_next(dec_ctx, *in++); + if (codepoint < 0) { + if (codepoint == DUK__CP_CONTINUE) { + continue; + } + + /* Decoding error with or without retry. */ + DUK_ASSERT(codepoint == DUK__CP_ERROR || codepoint == DUK__CP_RETRY); + if (codepoint == DUK__CP_RETRY) { + --in; /* retry last byte */ + } + /* replacement mode: replace with U+FFFD */ + codepoint = DUK_UNICODE_CP_REPLACEMENT_CHARACTER; + if (dec_ctx->fatal) { + /* fatal mode: throw a TypeError */ + goto fail_type; + } + /* Continue with 'codepoint', Unicode replacement. */ + } + DUK_ASSERT(codepoint >= 0x0000L && codepoint <= 0x10ffffL); + + if (!dec_ctx->bom_handled) { + dec_ctx->bom_handled = 1; + if (codepoint == 0xfeffL && !dec_ctx->ignore_bom) { + continue; + } + } + + out += duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, out); + DUK_ASSERT(out <= output + (3 + (3 * len))); + } + + if (!stream) { + if (dec_ctx->needed != 0) { + /* truncated sequence at end of buffer */ + if (dec_ctx->fatal) { + goto fail_type; + } else { + out += duk_unicode_encode_cesu8(DUK_UNICODE_CP_REPLACEMENT_CHARACTER, out); + DUK_ASSERT(out <= output + (3 + (3 * len))); + } + } + duk__utf8_decode_init(dec_ctx); /* Initialize decoding state for potential reuse. */ + } + + /* Output buffer is fixed and thus stable even if there had been + * side effects (which there shouldn't be). + */ + duk_push_lstring(thr, (const char *) output, (duk_size_t) (out - output)); + return 1; + + fail_type: + DUK_ERROR_TYPE(thr, DUK_STR_UTF8_DECODE_FAILED); + DUK_WO_NORETURN(return 0;); +} + +/* + * Built-in bindings + */ + +#if defined(DUK_USE_ENCODING_BUILTINS) +DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_hthread *thr) { + /* TextEncoder currently requires no persistent state, so the constructor + * does nothing on purpose. + */ + + duk_require_constructor_call(thr); + return 0; +} + +DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_hthread *thr) { + duk_push_literal(thr, "utf-8"); + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_hthread *thr) { + duk__encode_context enc_ctx; + duk_size_t len; + duk_size_t final_len; + duk_uint8_t *output; + + DUK_ASSERT_TOP(thr, 1); + if (duk_is_undefined(thr, 0)) { + len = 0; + } else { + duk_hstring *h_input; + + h_input = duk_to_hstring(thr, 0); + DUK_ASSERT(h_input != NULL); + + len = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_input); + if (len >= DUK_HBUFFER_MAX_BYTELEN / 3) { + DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG); + DUK_WO_NORETURN(return 0;); + } + } + + /* Allowance is 3*len because all bytes can potentially be replaced with + * U+FFFD -- which rather inconveniently encodes to 3 bytes in UTF-8. + * Rely on dynamic buffer data pointer stability: no other code has + * access to the data pointer. + * + * XXX: The buffer allocation strategy used here is rather inefficient. + * Maybe switch to a chunk-based strategy, or preprocess the string to + * figure out the space needed ahead of time? + */ + DUK_ASSERT(3 * len >= len); + output = (duk_uint8_t *) duk_push_dynamic_buffer(thr, 3 * len); + + if (len > 0) { + DUK_ASSERT(duk_is_string(thr, 0)); /* True if len > 0. */ + + /* XXX: duk_decode_string() is used to process the input + * string. For standard ECMAScript strings, represented + * internally as CESU-8, this is fine. However, behavior + * beyond CESU-8 is not very strict: codepoints using an + * extended form of UTF-8 are also accepted, and invalid + * codepoint sequences (which are allowed in Duktape strings) + * are not handled as well as they could (e.g. invalid + * continuation bytes may mask following codepoints). + * This is how ECMAScript code would also see such strings. + * Maybe replace duk_decode_string() with an explicit strict + * CESU-8 decoder here? + */ + enc_ctx.lead = 0x0000L; + enc_ctx.out = output; + duk_decode_string(thr, 0, duk__utf8_encode_char, (void *) &enc_ctx); + if (enc_ctx.lead != 0x0000L) { + /* unpaired high surrogate at end of string */ + enc_ctx.out = duk__utf8_emit_repl(enc_ctx.out); + DUK_ASSERT(enc_ctx.out <= output + (3 * len)); + } + + /* The output buffer is usually very much oversized, so shrink it to + * actually needed size. Pointer stability assumed up to this point. + */ + DUK_ASSERT_TOP(thr, 2); + DUK_ASSERT(output == (duk_uint8_t *) duk_get_buffer_data(thr, -1, NULL)); + + final_len = (duk_size_t) (enc_ctx.out - output); + duk_resize_buffer(thr, -1, final_len); + /* 'output' and 'enc_ctx.out' are potentially invalidated by the resize. */ + } else { + final_len = 0; + } + + /* Standard WHATWG output is a Uint8Array. Here the Uint8Array will + * be backed by a dynamic buffer which differs from e.g. Uint8Arrays + * created as 'new Uint8Array(N)'. ECMAScript code won't see the + * difference but C code will. When bufferobjects are not supported, + * returns a plain dynamic buffer. + */ +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) + duk_push_buffer_object(thr, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY); +#endif + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_hthread *thr) { + duk__decode_context *dec_ctx; + duk_bool_t fatal = 0; + duk_bool_t ignore_bom = 0; + + DUK_ASSERT_TOP(thr, 2); + duk_require_constructor_call(thr); + if (!duk_is_undefined(thr, 0)) { + /* XXX: For now ignore 'label' (encoding identifier). */ + duk_to_string(thr, 0); + } + if (!duk_is_null_or_undefined(thr, 1)) { + if (duk_get_prop_literal(thr, 1, "fatal")) { + fatal = duk_to_boolean(thr, -1); + } + if (duk_get_prop_literal(thr, 1, "ignoreBOM")) { + ignore_bom = duk_to_boolean(thr, -1); + } + } + + duk_push_this(thr); + + /* The decode context is not assumed to be zeroed; all fields are + * initialized explicitly. + */ + dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(thr, sizeof(duk__decode_context)); + dec_ctx->fatal = (duk_uint8_t) fatal; + dec_ctx->ignore_bom = (duk_uint8_t) ignore_bom; + duk__utf8_decode_init(dec_ctx); /* Initializes remaining fields. */ + + duk_put_prop_literal(thr, -2, DUK_INTERNAL_SYMBOL("Context")); + return 0; +} + +/* Get TextDecoder context from 'this'; leaves garbage on stack. */ +DUK_LOCAL duk__decode_context *duk__get_textdecoder_context(duk_hthread *thr) { + duk__decode_context *dec_ctx; + duk_push_this(thr); + duk_get_prop_literal(thr, -1, DUK_INTERNAL_SYMBOL("Context")); + dec_ctx = (duk__decode_context *) duk_require_buffer(thr, -1, NULL); + DUK_ASSERT(dec_ctx != NULL); + return dec_ctx; +} + +DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_hthread *thr) { + duk__decode_context *dec_ctx; + duk_int_t magic; + + dec_ctx = duk__get_textdecoder_context(thr); + magic = duk_get_current_magic(thr); + switch (magic) { + case 0: + /* Encoding is now fixed, so _Context lookup is only needed to + * validate the 'this' binding (TypeError if not TextDecoder-like). + */ + duk_push_literal(thr, "utf-8"); + break; + case 1: + duk_push_boolean(thr, dec_ctx->fatal); + break; + default: + duk_push_boolean(thr, dec_ctx->ignore_bom); + break; + } + + return 1; +} + +DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_hthread *thr) { + duk__decode_context *dec_ctx; + + dec_ctx = duk__get_textdecoder_context(thr); + return duk__decode_helper(thr, dec_ctx); +} +#endif /* DUK_USE_ENCODING_BUILTINS */ + +/* + * Internal helper for Node.js Buffer + */ + +/* Internal helper used for Node.js Buffer .toString(). Value stack convention + * is currently odd: it mimics TextDecoder .decode() so that argument must be at + * index 0, and decode options (not present for Buffer) at index 1. Return value + * is a Duktape/C function return value. + */ +DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr) { + duk__decode_context dec_ctx; + + dec_ctx.fatal = 0; /* use replacement chars */ + dec_ctx.ignore_bom = 1; /* ignore BOMs (matches Node.js Buffer .toString()) */ + duk__utf8_decode_init(&dec_ctx); + + return duk__decode_helper(thr, &dec_ctx); +} + +/* automatic undefs */ +#undef DUK__CP_CONTINUE +#undef DUK__CP_ERROR +#undef DUK__CP_RETRY /* * Error built-ins */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_hthread *thr) { /* Behavior for constructor and non-constructor call is * the same except for augmenting the created error. When * called as a constructor, the caller (duk_new()) will handle @@ -26883,53 +34786,51 @@ DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { * it here. */ - duk_hthread *thr = (duk_hthread *) ctx; - duk_small_int_t bidx_prototype = duk_get_current_magic(ctx); + duk_small_int_t bidx_prototype = duk_get_current_magic(thr); /* same for both error and each subclass like TypeError */ duk_uint_t flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR); - DUK_UNREF(thr); - - duk_push_object_helper(ctx, flags_and_class, bidx_prototype); + (void) duk_push_object_helper(thr, flags_and_class, bidx_prototype); /* If message is undefined, the own property 'message' is not set at * all to save property space. An empty message is inherited anyway. */ - if (!duk_is_undefined(ctx, 0)) { - duk_to_string(ctx, 0); - duk_dup(ctx, 0); /* [ message error message ] */ - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); + if (!duk_is_undefined(thr, 0)) { + duk_to_string(thr, 0); + duk_dup_0(thr); /* [ message error message ] */ + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC); } /* Augment the error if called as a normal function. __FILE__ and __LINE__ * are not desirable in this case. */ -#ifdef DUK_USE_AUGMENT_ERROR_CREATE - if (!duk_is_constructor_call(ctx)) { - duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/); +#if defined(DUK_USE_AUGMENT_ERROR_CREATE) + if (!duk_is_constructor_call(thr)) { + duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE); } #endif return 1; } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_hthread *thr) { /* XXX: optimize with more direct internal access */ - duk_push_this(ctx); - (void) duk_require_hobject_or_lfunc_coerce(ctx, -1); + duk_push_this(thr); + (void) duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); /* [ ... this ] */ - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); - if (duk_is_undefined(ctx, -1)) { - duk_pop(ctx); - duk_push_string(ctx, "Error"); + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME); + if (duk_is_undefined(thr, -1)) { + duk_pop(thr); + duk_push_literal(thr, "Error"); } else { - duk_to_string(ctx, -1); + duk_to_string(thr, -1); } /* [ ... this name ] */ @@ -26938,28 +34839,28 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { * accident or are they actually needed? The first ToString() * could conceivably return 'undefined'. */ - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE); - if (duk_is_undefined(ctx, -1)) { - duk_pop(ctx); - duk_push_string(ctx, ""); + duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE); + if (duk_is_undefined(thr, -1)) { + duk_pop(thr); + duk_push_hstring_empty(thr); } else { - duk_to_string(ctx, -1); + duk_to_string(thr, -1); } /* [ ... this name message ] */ - if (duk_get_length(ctx, -2) == 0) { + if (duk_get_length(thr, -2) == 0) { /* name is empty -> return message */ return 1; } - if (duk_get_length(ctx, -1) == 0) { + if (duk_get_length(thr, -1) == 0) { /* message is empty -> return name */ - duk_pop(ctx); + duk_pop(thr); return 1; } - duk_push_string(ctx, ": "); - duk_insert(ctx, -2); /* ... name ': ' message */ - duk_concat(ctx, 3); + duk_push_literal(thr, ": "); + duk_insert(thr, -2); /* ... name ': ' message */ + duk_concat(thr, 3); return 1; } @@ -26985,8 +34886,7 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { #define DUK__OUTPUT_TYPE_FILENAME 0 #define DUK__OUTPUT_TYPE_LINENUMBER 1 -DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_hthread *thr, duk_small_int_t output_type) { duk_idx_t idx_td; duk_small_int_t i; /* traceback depth fits into 16 bits */ duk_small_int_t t; /* stack type fits into 16 bits */ @@ -26998,56 +34898,57 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o const char *str_directeval = " directeval"; const char *str_empty = ""; - DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ - DUK_UNREF(thr); + DUK_ASSERT_TOP(thr, 0); /* fixed arg count */ - duk_push_this(ctx); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TRACEDATA); - idx_td = duk_get_top_index(ctx); + duk_push_this(thr); + duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_TRACEDATA); + idx_td = duk_get_top_index(thr); - duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE); - duk_push_this(ctx); + duk_push_hstring_stridx(thr, DUK_STRIDX_NEWLINE_4SPACE); + duk_push_this(thr); /* [ ... this tracedata sep this ] */ /* XXX: skip null filename? */ - if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) { + if (duk_check_type(thr, idx_td, DUK_TYPE_OBJECT)) { /* Current tracedata contains 2 entries per callstack entry. */ for (i = 0; ; i += 2) { duk_int_t pc; - duk_int_t line; - duk_int_t flags; + duk_uint_t line; + duk_uint_t flags; duk_double_t d; const char *funcname; const char *filename; duk_hobject *h_func; duk_hstring *h_name; - duk_require_stack(ctx, 5); - duk_get_prop_index(ctx, idx_td, i); - duk_get_prop_index(ctx, idx_td, i + 1); - d = duk_to_number(ctx, -1); - pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32); - flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32); - t = (duk_small_int_t) duk_get_type(ctx, -2); + duk_require_stack(thr, 5); + duk_get_prop_index(thr, idx_td, (duk_uarridx_t) i); + duk_get_prop_index(thr, idx_td, (duk_uarridx_t) (i + 1)); + d = duk_to_number_m1(thr); + pc = duk_double_to_int_t(DUK_FMOD(d, DUK_DOUBLE_2TO32)); + flags = duk_double_to_uint_t(DUK_FLOOR(d / DUK_DOUBLE_2TO32)); + t = (duk_small_int_t) duk_get_type(thr, -2); if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) { /* - * Ecmascript/native function call or lightfunc call + * ECMAScript/native function call or lightfunc call */ count_func++; /* [ ... v1(func) v2(pc+flags) ] */ - h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */ - - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); - duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME); + /* These may be systematically omitted by Duktape + * with certain config options, but allow user to + * set them on a case-by-case basis. + */ + duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME); + duk_get_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME); #if defined(DUK_USE_PC2LINE) - line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc); + line = (duk_uint_t) duk_hobject_pc2line_query(thr, -4, (duk_uint_fast32_t) pc); #else line = 0; #endif @@ -27057,35 +34958,37 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o /* When looking for .fileName/.lineNumber, blame first * function which has a .fileName. */ - if (duk_is_string(ctx, -1)) { + if (duk_is_string_notsymbol(thr, -1)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { - duk_push_int(ctx, line); + duk_push_uint(thr, line); return 1; } } /* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */ /* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */ - h_name = duk_get_hstring(ctx, -2); /* may be NULL */ + h_name = duk_get_hstring_notsymbol(thr, -2); /* may be NULL */ funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ? "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name); - filename = duk_get_string(ctx, -1); + filename = duk_get_string_notsymbol(thr, -1); filename = filename ? filename : ""; DUK_ASSERT(funcname != NULL); DUK_ASSERT(filename != NULL); + h_func = duk_get_hobject(thr, -4); /* NULL for lightfunc */ + if (h_func == NULL) { - duk_push_sprintf(ctx, "at %s light%s%s%s%s%s", + duk_push_sprintf(thr, "at %s light%s%s%s%s%s", (const char *) funcname, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); - } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) { - duk_push_sprintf(ctx, "at %s (%s) native%s%s%s%s%s", + } else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) { + duk_push_sprintf(thr, "at %s (%s) native%s%s%s%s%s", (const char *) funcname, (const char *) filename, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), @@ -27094,19 +34997,21 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } else { - duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s", + duk_push_sprintf(thr, "at %s (%s:%lu)%s%s%s%s%s", (const char *) funcname, (const char *) filename, - (long) line, + (unsigned long) line, (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty), (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty), (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty), (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty), (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty)); } - duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ - duk_pop_n(ctx, 3); /* -> [ ... str ] */ + duk_replace(thr, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */ + duk_pop_3(thr); /* -> [ ... str ] */ } else if (t == DUK_TYPE_STRING) { + const char *str_file; + /* * __FILE__ / __LINE__ entry, here 'pc' is line number directly. * Sometimes __FILE__ / __LINE__ is reported as the source for @@ -27120,21 +35025,27 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o */ if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) { if (output_type == DUK__OUTPUT_TYPE_FILENAME) { - duk_pop(ctx); + duk_pop(thr); return 1; } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) { - duk_push_int(ctx, pc); + duk_push_int(thr, pc); return 1; } } - duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal", - (const char *) duk_get_string(ctx, -2), (long) pc); - duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ - duk_pop(ctx); /* -> [ ... str ] */ + /* Tracedata is trusted but avoid any risk of using a NULL + * for %s format because it has undefined behavior. Symbols + * don't need to be explicitly rejected as they pose no memory + * safety issues. + */ + str_file = (const char *) duk_get_string(thr, -2); + duk_push_sprintf(thr, "at [anon] (%s:%ld) internal", + (const char *) (str_file ? str_file : "null"), (long) pc); + duk_replace(thr, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */ + duk_pop(thr); /* -> [ ... str ] */ } else { /* unknown, ignore */ - duk_pop_2(ctx); + duk_pop_2(thr); break; } } @@ -27144,7 +35055,7 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * marker so this is the best we can do. */ - duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS); + duk_push_hstring_stridx(thr, DUK_STRIDX_BRACKETED_ELLIPSIS); } } @@ -27157,7 +35068,7 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * duk_join() automatically. We don't want to do that * coercion when providing .fileName or .lineNumber (GH-254). */ - duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/); + duk_join(thr, duk_get_top(thr) - (idx_td + 2) /*count, not including sep*/); return 1; } } @@ -27166,22 +35077,18 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * save space. For setters the stridx could be encoded into 'magic'. */ -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) { - return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_TRACEBACK); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) { + return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_TRACEBACK); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) { - return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_FILENAME); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) { + return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_FILENAME); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) { - return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_LINENUMBER); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) { + return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_LINENUMBER); } -#undef DUK__OUTPUT_TYPE_TRACEBACK -#undef DUK__OUTPUT_TYPE_FILENAME -#undef DUK__OUTPUT_TYPE_LINENUMBER - #else /* DUK_USE_TRACEBACKS */ /* @@ -27196,26 +35103,26 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx * of the error so this makes sense. */ -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) { /* XXX: remove this native function and map 'stack' accessor * to the toString() implementation directly. */ - return duk_bi_error_prototype_to_string(ctx); + return duk_bi_error_prototype_to_string(thr); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) { - DUK_UNREF(ctx); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) { + DUK_UNREF(thr); return 0; } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) { - DUK_UNREF(ctx); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) { + DUK_UNREF(thr); return 0; } #endif /* DUK_USE_TRACEBACKS */ -DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_context *ctx, duk_small_uint_t stridx_key) { +DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_hthread *thr, duk_small_uint_t stridx_key) { /* Attempt to write 'stack', 'fileName', 'lineNumber' works as if * user code called Object.defineProperty() to create an overriding * own property. This allows user code to overwrite .fileName etc @@ -27223,102 +35130,118 @@ DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_context *ctx, duk_small_uint_t * See https://github.com/svaarala/duktape/issues/387. */ - DUK_ASSERT_TOP(ctx, 1); /* fixed arg count: value */ + DUK_ASSERT_TOP(thr, 1); /* fixed arg count: value */ - duk_push_this(ctx); - duk_push_hstring_stridx(ctx, (duk_small_int_t) stridx_key); - duk_dup(ctx, 0); + duk_push_this(thr); + duk_push_hstring_stridx(thr, stridx_key); + duk_dup_0(thr); /* [ ... obj key value ] */ DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T", - duk_get_tval(ctx, -3), duk_get_tval(ctx, -2), duk_get_tval(ctx, -1))); + duk_get_tval(thr, -3), duk_get_tval(thr, -2), duk_get_tval(thr, -1))); - duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | + duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE | /*not enumerable*/ DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE); return 0; } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx) { - return duk__error_setter_helper(ctx, DUK_STRIDX_STACK); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_hthread *thr) { + return duk__error_setter_helper(thr, DUK_STRIDX_STACK); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_context *ctx) { - return duk__error_setter_helper(ctx, DUK_STRIDX_FILE_NAME); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_hthread *thr) { + return duk__error_setter_helper(thr, DUK_STRIDX_FILE_NAME); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx) { - return duk__error_setter_helper(ctx, DUK_STRIDX_LINE_NUMBER); +DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_hthread *thr) { + return duk__error_setter_helper(thr, DUK_STRIDX_LINE_NUMBER); } -#line 1 "duk_bi_function.c" + +/* automatic undefs */ +#undef DUK__OUTPUT_TYPE_FILENAME +#undef DUK__OUTPUT_TYPE_LINENUMBER +#undef DUK__OUTPUT_TYPE_TRACEBACK /* * Function built-ins */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ -DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +/* Needed even when Function built-in is disabled. */ +DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_hthread *thr) { + /* ignore arguments, return undefined (E5 Section 15.3.4) */ + DUK_UNREF(thr); + return 0; +} + +#if defined(DUK_USE_FUNCTION_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_hthread *thr) { duk_hstring *h_sourcecode; duk_idx_t nargs; duk_idx_t i; duk_small_uint_t comp_flags; - duk_hcompiledfunction *func; + duk_hcompfunc *func; duk_hobject *outer_lex_env; duk_hobject *outer_var_env; /* normal and constructor calls have identical semantics */ - nargs = duk_get_top(ctx); + nargs = duk_get_top(thr); for (i = 0; i < nargs; i++) { - duk_to_string(ctx, i); + duk_to_string(thr, i); /* Rejects Symbols during coercion. */ } if (nargs == 0) { - duk_push_string(ctx, ""); - duk_push_string(ctx, ""); + duk_push_hstring_empty(thr); + duk_push_hstring_empty(thr); } else if (nargs == 1) { /* XXX: cover this with the generic >1 case? */ - duk_push_string(ctx, ""); + duk_push_hstring_empty(thr); } else { - duk_insert(ctx, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ - duk_push_string(ctx, ","); - duk_insert(ctx, 1); - duk_join(ctx, nargs - 1); + duk_insert(thr, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ + duk_push_literal(thr, ","); + duk_insert(thr, 1); + duk_join(thr, nargs - 1); } /* [ body formals ], formals is comma separated list that needs to be parsed */ - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); /* XXX: this placeholder is not always correct, but use for now. * It will fail in corner cases; see test-dev-func-cons-args.js. */ - duk_push_string(ctx, "function("); - duk_dup(ctx, 1); - duk_push_string(ctx, "){"); - duk_dup(ctx, 0); - duk_push_string(ctx, "}"); - duk_concat(ctx, 5); + duk_push_literal(thr, "function("); + duk_dup_1(thr); + duk_push_literal(thr, "){"); + duk_dup_0(thr); + duk_push_literal(thr, "\n}"); /* Newline is important to handle trailing // comment. */ + duk_concat(thr, 5); /* [ body formals source ] */ - DUK_ASSERT_TOP(ctx, 3); + DUK_ASSERT_TOP(thr, 3); /* strictness is not inherited, intentional */ - comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; + comp_flags = DUK_COMPILE_FUNCEXPR; - duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ - h_sourcecode = duk_require_hstring(ctx, -2); + duk_push_hstring_stridx(thr, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ + h_sourcecode = duk_require_hstring(thr, -2); /* no symbol check needed; -2 is concat'd code */ duk_js_compile(thr, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), comp_flags); - func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); + + /* Force .name to 'anonymous' (ES2015). */ + duk_push_literal(thr, "anonymous"); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); + + func = (duk_hcompfunc *) duk_known_hobject(thr, -1); + DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func)); + DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) func)); /* [ body formals source template ] */ @@ -27335,36 +35258,34 @@ DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { return 1; } +#endif /* DUK_USE_FUNCTION_BUILTIN */ -DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_context *ctx) { - /* ignore arguments, return undefined (E5 Section 15.3.4) */ - DUK_UNREF(ctx); - return 0; -} - -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { +#if defined(DUK_USE_FUNCTION_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_hthread *thr) { duk_tval *tv; /* * E5 Section 15.3.4.2 places few requirements on the output of - * this function: + * this function: the result is implementation dependent, must + * follow FunctionDeclaration syntax (in particular, must have a + * name even for anonymous functions or functions with empty name). + * The output does NOT need to compile into anything useful. * - * - The result is an implementation dependent representation - * of the function; in particular + * E6 Section 19.2.3.5 changes the requirements completely: the + * result must either eval() to a functionally equivalent object + * OR eval() to a SyntaxError. * - * - The result must follow the syntax of a FunctionDeclaration. - * In particular, the function must have a name (even in the - * case of an anonymous function or a function with an empty - * name). + * We opt for the SyntaxError approach for now, with a syntax that + * mimics V8's native function syntax: * - * - Note in particular that the output does NOT need to compile - * into anything useful. + * 'function cos() { [native code] }' + * + * but extended with [ecmascript code], [bound code], and + * [lightfunc code]. */ - - /* XXX: faster internal way to get this */ - duk_push_this(ctx); - tv = duk_get_tval(ctx, -1); + duk_push_this(thr); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_OBJECT(tv)) { @@ -27372,33 +35293,31 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { const char *func_name; /* Function name: missing/undefined is mapped to empty string, - * otherwise coerce to string. + * otherwise coerce to string. No handling for invalid identifier + * characters or e.g. '{' in the function name. This doesn't + * really matter as long as a SyntaxError results. Technically + * if the name contained a suitable prefix followed by '//' it + * might cause the result to parse without error. */ - /* XXX: currently no handling for non-allowed identifier characters, - * e.g. a '{' in the function name. - */ - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); - if (duk_is_undefined(ctx, -1)) { + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME); + if (duk_is_undefined(thr, -1)) { func_name = ""; } else { - func_name = duk_to_string(ctx, -1); + func_name = duk_to_string(thr, -1); DUK_ASSERT(func_name != NULL); } - /* Indicate function type in the function body using a dummy - * directive. - */ - if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { - duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name); - } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { - duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name); - } else if (DUK_HOBJECT_HAS_BOUND(obj)) { - duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name); + if (DUK_HOBJECT_IS_COMPFUNC(obj)) { + duk_push_sprintf(thr, "function %s() { [ecmascript code] }", (const char *) func_name); + } else if (DUK_HOBJECT_IS_NATFUNC(obj)) { + duk_push_sprintf(thr, "function %s() { [native code] }", (const char *) func_name); + } else if (DUK_HOBJECT_IS_BOUNDFUNC(obj)) { + duk_push_sprintf(thr, "function %s() { [bound code] }", (const char *) func_name); } else { goto type_error; } } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { - duk_push_lightfunc_tostring(ctx, tv); + duk_push_lightfunc_tostring(thr, tv); } else { goto type_error; } @@ -27406,205 +35325,303 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { return 1; type_error: - return DUK_RET_TYPE_ERROR; + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } +#endif -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) { - duk_idx_t len; - duk_idx_t i; - - DUK_ASSERT_TOP(ctx, 2); /* not a vararg function */ - - duk_push_this(ctx); - if (!duk_is_callable(ctx, -1)) { - DUK_DDD(DUK_DDDPRINT("func is not callable")); - goto type_error; - } - duk_insert(ctx, 0); - DUK_ASSERT_TOP(ctx, 3); - - DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT", - (duk_tval *) duk_get_tval(ctx, 0), - (duk_tval *) duk_get_tval(ctx, 1), - (duk_tval *) duk_get_tval(ctx, 2))); - - /* [ func thisArg argArray ] */ - - if (duk_is_null_or_undefined(ctx, 2)) { - DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args")); - len = 0; - } else if (!duk_is_object(ctx, 2)) { - goto type_error; - } else { - DUK_DDD(DUK_DDDPRINT("argArray is an object")); - - /* XXX: make this an internal helper */ - duk_get_prop_stridx(ctx, 2, DUK_STRIDX_LENGTH); - len = (duk_idx_t) duk_to_uint32(ctx, -1); /* ToUint32() coercion required */ - duk_pop(ctx); - - duk_require_stack(ctx, len); - - DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len)); - for (i = 0; i < len; i++) { - duk_get_prop_index(ctx, 2, i); - } - } - duk_remove(ctx, 2); - DUK_ASSERT_TOP(ctx, 2 + len); - - /* [ func thisArg arg1 ... argN ] */ - - DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld", - (duk_tval *) duk_get_tval(ctx, 0), - (duk_tval *) duk_get_tval(ctx, 1), - (long) len)); - duk_call_method(ctx, len); - return 1; - - type_error: - return DUK_RET_TYPE_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) { - duk_idx_t nargs; - - /* Step 1 is not necessary because duk_call_method() will take - * care of it. - */ - - /* vararg function, thisArg needs special handling */ - nargs = duk_get_top(ctx); /* = 1 + arg count */ - if (nargs == 0) { - duk_push_undefined(ctx); - nargs++; - } - DUK_ASSERT(nargs >= 1); - - /* [ thisArg arg1 ... argN ] */ - - duk_push_this(ctx); /* 'func' in the algorithm */ - duk_insert(ctx, 0); - - /* [ func thisArg arg1 ... argN ] */ - - DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld", - (duk_tval *) duk_get_tval(ctx, 0), - (duk_tval *) duk_get_tval(ctx, 1), - (long) (nargs - 1), - (long) duk_get_top(ctx))); - duk_call_method(ctx, nargs - 1); - return 1; -} - -/* XXX: the implementation now assumes "chained" bound functions, - * whereas "collapsed" bound functions (where there is ever only - * one bound function which directly points to a non-bound, final - * function) would require a "collapsing" implementation which - * merges argument lists etc here. +/* Always present because the native function pointer is needed in call + * handling. */ -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) { - duk_hobject *h_bound; - duk_hobject *h_target; - duk_idx_t nargs; - duk_idx_t i; - - /* vararg function, careful arg handling (e.g. thisArg may not be present) */ - nargs = duk_get_top(ctx); /* = 1 + arg count */ - if (nargs == 0) { - duk_push_undefined(ctx); - nargs++; - } - DUK_ASSERT(nargs >= 1); - - duk_push_this(ctx); - if (!duk_is_callable(ctx, -1)) { - DUK_DDD(DUK_DDDPRINT("func is not callable")); - goto type_error; - } - - /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ - DUK_ASSERT_TOP(ctx, nargs + 1); - - /* create bound function object */ - duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BOUND | - DUK_HOBJECT_FLAG_CONSTRUCTABLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION), - DUK_BIDX_FUNCTION_PROTOTYPE); - h_bound = duk_get_hobject(ctx, -1); - DUK_ASSERT(h_bound != NULL); - - /* [ thisArg arg1 ... argN func boundFunc ] */ - duk_dup(ctx, -2); /* func */ - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); - - duk_dup(ctx, 0); /* thisArg */ - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); - - duk_push_array(ctx); - - /* [ thisArg arg1 ... argN func boundFunc argArray ] */ - - for (i = 0; i < nargs - 1; i++) { - duk_dup(ctx, 1 + i); - duk_put_prop_index(ctx, -2, i); - } - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE); - - /* [ thisArg arg1 ... argN func boundFunc ] */ - - /* bound function 'length' property is interesting */ - h_target = duk_get_hobject(ctx, -2); - if (h_target == NULL || /* lightfunc */ - DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) { - /* For lightfuncs, simply read the virtual property. */ - duk_int_t tmp; - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH); - tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */ - duk_pop(ctx); - duk_push_int(ctx, (tmp < 0 ? 0 : tmp)); - } else { - duk_push_int(ctx, 0); - } - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */ - - /* caller and arguments must use the same thrower, [[ThrowTypeError]] */ - duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); - duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE); - - /* these non-standard properties are copied for convenience */ - /* XXX: 'copy properties' API call? */ - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); - - /* The 'strict' flag is copied to get the special [[Get]] of E5.1 - * Section 15.3.5.4 to apply when a 'caller' value is a strict bound - * function. Not sure if this is correct, because the specification - * is a bit ambiguous on this point but it would make sense. +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_hthread *thr) { + /* .call() is dealt with in call handling by simulating its + * effects so this function is actually never called. */ - if (h_target == NULL) { - /* Lightfuncs are always strict. */ - DUK_HOBJECT_SET_STRICT(h_bound); - } else if (DUK_HOBJECT_HAS_STRICT(h_target)) { - DUK_HOBJECT_SET_STRICT(h_bound); - } - DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1))); - - return 1; - - type_error: + DUK_UNREF(thr); return DUK_RET_TYPE_ERROR; } -#line 1 "duk_bi_global.c" + +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_hthread *thr) { + /* Like .call(), never actually called. */ + DUK_UNREF(thr); + return DUK_RET_TYPE_ERROR; +} + +DUK_INTERNAL duk_ret_t duk_bi_reflect_apply(duk_hthread *thr) { + /* Like .call(), never actually called. */ + DUK_UNREF(thr); + return DUK_RET_TYPE_ERROR; +} + +DUK_INTERNAL duk_ret_t duk_bi_reflect_construct(duk_hthread *thr) { + /* Like .call(), never actually called. */ + DUK_UNREF(thr); + return DUK_RET_TYPE_ERROR; +} + +#if defined(DUK_USE_FUNCTION_BUILTIN) +/* Create a bound function which points to a target function which may + * be bound or non-bound. If the target is bound, the argument lists + * and 'this' binding of the functions are merged and the resulting + * function points directly to the non-bound target. + */ +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_hthread *thr) { + duk_hboundfunc *h_bound; + duk_idx_t nargs; /* bound args, not counting 'this' binding */ + duk_idx_t bound_nargs; + duk_int_t bound_len; + duk_tval *tv_prevbound; + duk_idx_t n_prevbound; + duk_tval *tv_res; + duk_tval *tv_tmp; + + /* XXX: C API call, e.g. duk_push_bound_function(thr, target_idx, nargs); */ + + /* Vararg function, careful arg handling, e.g. thisArg may not + * be present. + */ + nargs = duk_get_top(thr) - 1; /* actual args, not counting 'this' binding */ + if (nargs < 0) { + nargs++; + duk_push_undefined(thr); + } + DUK_ASSERT(nargs >= 0); + + /* Limit 'nargs' for bound functions to guarantee arithmetic + * below will never wrap. + */ + if (nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) { + DUK_DCERROR_RANGE_INVALID_COUNT(thr); + } + + duk_push_this(thr); + duk_require_callable(thr, -1); + + /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs+1 total) */ + DUK_ASSERT_TOP(thr, nargs + 2); + + /* Create bound function object. */ + h_bound = duk_push_hboundfunc(thr); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->target)); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->this_binding)); + DUK_ASSERT(h_bound->args == NULL); + DUK_ASSERT(h_bound->nargs == 0); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_bound) == NULL); + + /* [ thisArg arg1 ... argN func boundFunc ] */ + + /* If the target is a bound function, argument lists must be + * merged. The 'this' binding closest to the target function + * wins because in call handling the 'this' gets replaced over + * and over again until we call the non-bound function. + */ + tv_prevbound = NULL; + n_prevbound = 0; + tv_tmp = DUK_GET_TVAL_POSIDX(thr, 0); + DUK_TVAL_SET_TVAL(&h_bound->this_binding, tv_tmp); + tv_tmp = DUK_GET_TVAL_NEGIDX(thr, -2); + DUK_TVAL_SET_TVAL(&h_bound->target, tv_tmp); + + if (DUK_TVAL_IS_OBJECT(tv_tmp)) { + duk_hobject *h_target; + duk_hobject *bound_proto; + + h_target = DUK_TVAL_GET_OBJECT(tv_tmp); + DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(h_target)); + + /* Internal prototype must be copied from the target. + * For lightfuncs Function.prototype is used and is already + * in place. + */ + bound_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_target); + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto); + + /* The 'strict' flag is copied to get the special [[Get]] of E5.1 + * Section 15.3.5.4 to apply when a 'caller' value is a strict bound + * function. Not sure if this is correct, because the specification + * is a bit ambiguous on this point but it would make sense. + */ + /* Strictness is inherited from target. */ + if (DUK_HOBJECT_HAS_STRICT(h_target)) { + DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound); + } + + if (DUK_HOBJECT_HAS_BOUNDFUNC(h_target)) { + duk_hboundfunc *h_boundtarget; + + h_boundtarget = (duk_hboundfunc *) (void *) h_target; + + /* The final function should always be non-bound, unless + * there's a bug in the internals. Assert for it. + */ + DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h_boundtarget->target) || + (DUK_TVAL_IS_OBJECT(&h_boundtarget->target) && + DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)) && + !DUK_HOBJECT_IS_BOUNDFUNC(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)))); + + DUK_TVAL_SET_TVAL(&h_bound->target, &h_boundtarget->target); + DUK_TVAL_SET_TVAL(&h_bound->this_binding, &h_boundtarget->this_binding); + + tv_prevbound = h_boundtarget->args; + n_prevbound = h_boundtarget->nargs; + } + } else { + /* Lightfuncs are always strict. */ + duk_hobject *bound_proto; + + DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_tmp)); + DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound); + bound_proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]; + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto); + } + + DUK_TVAL_INCREF(thr, &h_bound->target); /* old values undefined, no decref needed */ + DUK_TVAL_INCREF(thr, &h_bound->this_binding); + + bound_nargs = n_prevbound + nargs; + if (bound_nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) { + DUK_DCERROR_RANGE_INVALID_COUNT(thr); + } + tv_res = (duk_tval *) DUK_ALLOC_CHECKED(thr, ((duk_size_t) bound_nargs) * sizeof(duk_tval)); + DUK_ASSERT(tv_res != NULL || bound_nargs == 0); + DUK_ASSERT(h_bound->args == NULL); + DUK_ASSERT(h_bound->nargs == 0); + h_bound->args = tv_res; + h_bound->nargs = bound_nargs; + + DUK_ASSERT(n_prevbound >= 0); + duk_copy_tvals_incref(thr, tv_res, tv_prevbound, (duk_size_t) n_prevbound); + DUK_ASSERT(nargs >= 0); + duk_copy_tvals_incref(thr, tv_res + n_prevbound, DUK_GET_TVAL_POSIDX(thr, 1), (duk_size_t) nargs); + + /* [ thisArg arg1 ... argN func boundFunc ] */ + + /* Bound function 'length' property is interesting. + * For lightfuncs, simply read the virtual property. + */ + duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH); + bound_len = duk_get_int(thr, -1); /* ES2015: no coercion */ + if (bound_len < nargs) { + bound_len = 0; + } else { + bound_len -= nargs; + } + if (sizeof(duk_int_t) > 4 && bound_len > (duk_int_t) DUK_UINT32_MAX) { + bound_len = (duk_int_t) DUK_UINT32_MAX; + } + duk_pop(thr); + DUK_ASSERT(bound_len >= 0); + tv_tmp = thr->valstack_top++; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_tmp)); + DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_tmp)); + DUK_TVAL_SET_U32(tv_tmp, (duk_uint32_t) bound_len); /* in-place update, fastint */ + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); /* attrs in E6 Section 9.2.4 */ + + /* XXX: could these be virtual? */ + /* Caller and arguments must use the same thrower, [[ThrowTypeError]]. */ + duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_CALLER); + duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_LC_ARGUMENTS); + + /* Function name and fileName (non-standard). */ + duk_push_literal(thr, "bound "); /* ES2015 19.2.3.2. */ + duk_get_prop_stridx(thr, -3, DUK_STRIDX_NAME); + if (!duk_is_string_notsymbol(thr, -1)) { + /* ES2015 has requirement to check that .name of target is a string + * (also must check for Symbol); if not, targetName should be the + * empty string. ES2015 19.2.3.2. + */ + duk_pop(thr); + duk_push_hstring_empty(thr); + } + duk_concat(thr, 2); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); +#if defined(DUK_USE_FUNC_FILENAME_PROPERTY) + duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C); +#endif + + DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(thr, -1))); + + return 1; +} +#endif /* DUK_USE_FUNCTION_BUILTIN */ + +/* %NativeFunctionPrototype% .length getter. */ +DUK_INTERNAL duk_ret_t duk_bi_native_function_length(duk_hthread *thr) { + duk_tval *tv; + duk_hnatfunc *h; + duk_int16_t func_nargs; + + tv = duk_get_borrowed_this_tval(thr); + DUK_ASSERT(tv != NULL); + + if (DUK_TVAL_IS_OBJECT(tv)) { + h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) { + goto fail_type; + } + func_nargs = h->nargs; + duk_push_int(thr, func_nargs == DUK_HNATFUNC_NARGS_VARARGS ? 0 : func_nargs); + } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { + duk_small_uint_t lf_flags; + duk_small_uint_t lf_len; + + lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv); + lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags); + duk_push_uint(thr, lf_len); + } else { + goto fail_type; + } + return 1; + + fail_type: + DUK_DCERROR_TYPE_INVALID_ARGS(thr); +} + +/* %NativeFunctionPrototype% .name getter. */ +DUK_INTERNAL duk_ret_t duk_bi_native_function_name(duk_hthread *thr) { + duk_tval *tv; + duk_hnatfunc *h; + + tv = duk_get_borrowed_this_tval(thr); + DUK_ASSERT(tv != NULL); + + if (DUK_TVAL_IS_OBJECT(tv)) { + h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(h != NULL); + if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) { + goto fail_type; + } +#if 0 + duk_push_hnatfunc_name(thr, h); +#endif + duk_push_hstring_empty(thr); + } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { + duk_push_lightfunc_name(thr, tv); + } else { + goto fail_type; + } + return 1; + + fail_type: + DUK_DCERROR_TYPE_INVALID_ARGS(thr); +} + +#if defined(DUK_USE_SYMBOL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_function_prototype_hasinstance(duk_hthread *thr) { + /* This binding: RHS, stack index 0: LHS. */ + duk_bool_t ret; + + ret = duk_js_instanceof_ordinary(thr, DUK_GET_TVAL_POSIDX(thr, 0), DUK_GET_THIS_TVAL_PTR(thr)); + duk_push_boolean(thr, ret); + return 1; +} +#endif /* DUK_USE_SYMBOL_BUILTIN */ /* * Global object built-ins */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ /* * Encoding/decoding helpers @@ -27672,7 +35689,7 @@ DUK_LOCAL const duk_uint8_t duk__decode_uri_component_reserved_table[16] = { DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x70-0x7f */ }; -#ifdef DUK_USE_SECTION_B +#if defined(DUK_USE_SECTION_B) /* E5.1 Section B.2.2, step 7. */ DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = { DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), DUK__MKBITS(0, 0, 0, 0, 0, 0, 0, 0), /* 0x00-0x0f */ @@ -27686,8 +35703,6 @@ DUK_LOCAL const duk_uint8_t duk__escape_unescaped_table[16] = { }; #endif /* DUK_USE_SECTION_B */ -#undef DUK__MKBITS - typedef struct { duk_hthread *thr; duk_hstring *h_str; @@ -27717,15 +35732,14 @@ DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small return t; } -DUK_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback callback, const void *udata) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL int duk__transform_helper(duk_hthread *thr, duk__transform_callback callback, const void *udata) { duk__transform_context tfm_ctx_alloc; duk__transform_context *tfm_ctx = &tfm_ctx_alloc; duk_codepoint_t cp; tfm_ctx->thr = thr; - tfm_ctx->h_str = duk_to_hstring(ctx, 0); + tfm_ctx->h_str = duk_to_hstring(thr, 0); DUK_ASSERT(tfm_ctx->h_str != NULL); DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str)); /* initial size guess */ @@ -27741,7 +35755,7 @@ DUK_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback ca DUK_BW_COMPACT(thr, &tfm_ctx->bw); - duk_to_string(ctx, -1); + (void) duk_buffer_to_string(thr, -1); /* Safe if transform is safe. */ return 1; } @@ -27774,7 +35788,7 @@ DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ct goto uri_error; } cp1 = cp; - cp = ((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L; + cp = (duk_codepoint_t) (((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L); } else if (cp > 0x10ffffL) { /* Although we can allow non-BMP characters (they'll decode * back into surrogate pairs), we don't allow extended UTF-8 @@ -27793,7 +35807,7 @@ DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ct len = duk_unicode_encode_xutf8((duk_ucodepoint_t) cp, xutf8_buf); for (i = 0; i < len; i++) { - t = (int) xutf8_buf[i]; + t = (duk_small_int_t) xutf8_buf[i]; DUK_BW_WRITE_RAW_U8_3(tfm_ctx->thr, &tfm_ctx->bw, DUK_ASC_PERCENT, @@ -27804,7 +35818,8 @@ DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ct return; uri_error: - DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input"); + DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT); + DUK_WO_NORETURN(return;); } DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { @@ -27932,7 +35947,7 @@ DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ct DUK_ASSERT(cp < 0x100000L); DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L)); - DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffUL) + 0xdc00L)); + DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffL) + 0xdc00L)); } else { DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp); } @@ -27942,10 +35957,11 @@ DUK_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ct return; uri_error: - DUK_ERROR(tfm_ctx->thr, DUK_ERR_URI_ERROR, "invalid input"); + DUK_ERROR_URI(tfm_ctx->thr, DUK_STR_INVALID_INPUT); + DUK_WO_NORETURN(return;); } -#ifdef DUK_USE_SECTION_B +#if defined(DUK_USE_SECTION_B) DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { DUK_UNREF(udata); @@ -27982,7 +35998,8 @@ DUK_LOCAL void duk__transform_callback_escape(duk__transform_context *tfm_ctx, c return; esc_error: - DUK_ERROR_TYPE(tfm_ctx->thr, "invalid input"); + DUK_ERROR_TYPE(tfm_ctx->thr, DUK_STR_INVALID_INPUT); + DUK_WO_NORETURN(return;); } DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, const void *udata, duk_codepoint_t cp) { @@ -28015,27 +36032,27 @@ DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, * Eval needs to handle both a "direct eval" and an "indirect eval". * Direct eval handling needs access to the caller's activation so that its * lexical environment can be accessed. A direct eval is only possible from - * Ecmascript code; an indirect eval call is possible also from C code. + * ECMAScript code; an indirect eval call is possible also from C code. * When an indirect eval call is made from C code, there may not be a * calling activation at all which needs careful handling. */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_hthread *thr) { duk_hstring *h; duk_activation *act_caller; duk_activation *act_eval; - duk_activation *act; - duk_hcompiledfunction *func; + duk_hcompfunc *func; duk_hobject *outer_lex_env; duk_hobject *outer_var_env; duk_bool_t this_to_global = 1; duk_small_uint_t comp_flags; duk_int_t level = -2; + duk_small_uint_t call_flags; - DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2); /* 2 when called by debugger */ + DUK_ASSERT(duk_get_top(thr) == 1 || duk_get_top(thr) == 2); /* 2 when called by debugger */ DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */ - DUK_ASSERT(((thr->callstack + thr->callstack_top - 1)->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ + DUK_ASSERT(thr->callstack_curr != NULL); + DUK_ASSERT((thr->callstack_curr->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */ /* @@ -28046,99 +36063,96 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { * activation doesn't exist, call must be indirect. */ - h = duk_get_hstring(ctx, 0); + h = duk_get_hstring_notsymbol(thr, 0); if (!h) { + /* Symbol must be returned as is, like any non-string values. */ return 1; /* return arg as-is */ } #if defined(DUK_USE_DEBUGGER_SUPPORT) /* NOTE: level is used only by the debugger and should never be present - * for an Ecmascript eval(). + * for an ECMAScript eval(). */ DUK_ASSERT(level == -2); /* by default, use caller's environment */ - if (duk_get_top(ctx) >= 2 && duk_is_number(ctx, 1)) { - level = duk_get_int(ctx, 1); + if (duk_get_top(thr) >= 2 && duk_is_number(thr, 1)) { + level = duk_get_int(thr, 1); } DUK_ASSERT(level <= -2); /* This is guaranteed by debugger code. */ #endif /* [ source ] */ - comp_flags = DUK_JS_COMPILE_FLAG_EVAL; - act_eval = thr->callstack + thr->callstack_top - 1; /* this function */ - if (thr->callstack_top >= (duk_size_t) -level) { + comp_flags = DUK_COMPILE_EVAL; + act_eval = thr->callstack_curr; /* this function */ + DUK_ASSERT(act_eval != NULL); + act_caller = duk_hthread_get_activation_for_level(thr, level); + if (act_caller != NULL) { /* Have a calling activation, check for direct eval (otherwise * assume indirect eval. */ - act_caller = thr->callstack + thr->callstack_top + level; /* caller */ if ((act_caller->flags & DUK_ACT_FLAG_STRICT) && (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) { /* Only direct eval inherits strictness from calling code * (E5.1 Section 10.1.1). */ - comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; + comp_flags |= DUK_COMPILE_STRICT; } } else { DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0); } - act_caller = NULL; /* avoid dereference after potential callstack realloc */ - act_eval = NULL; - duk_push_hstring_stridx(ctx, DUK_STRIDX_INPUT); /* XXX: copy from caller? */ + duk_push_hstring_stridx(thr, DUK_STRIDX_INPUT); /* XXX: copy from caller? */ duk_js_compile(thr, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), (duk_size_t) DUK_HSTRING_GET_BYTELEN(h), comp_flags); - func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); - DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); + func = (duk_hcompfunc *) duk_known_hobject(thr, -1); + DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func)); /* [ source template ] */ /* E5 Section 10.4.2 */ - DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack + thr->callstack_top - 1; /* this function */ - if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) { + + if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) { DUK_ASSERT(thr->callstack_top >= 2); - act = thr->callstack + thr->callstack_top + level; /* caller */ - if (act->lex_env == NULL) { - DUK_ASSERT(act->var_env == NULL); + DUK_ASSERT(act_caller != NULL); + if (act_caller->lex_env == NULL) { + DUK_ASSERT(act_caller->var_env == NULL); DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); /* this may have side effects, so re-lookup act */ - duk_js_init_activation_environment_records_delayed(thr, act); - act = thr->callstack + thr->callstack_top + level; + duk_js_init_activation_environment_records_delayed(thr, act_caller); } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); + DUK_ASSERT(act_caller->lex_env != NULL); + DUK_ASSERT(act_caller->var_env != NULL); this_to_global = 0; if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) { - duk_hobject *new_env; + duk_hdecenv *new_env; duk_hobject *act_lex_env; DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> " "var_env and lex_env to a fresh env, " "this_binding to caller's this_binding")); - act = thr->callstack + thr->callstack_top + level; /* caller */ - act_lex_env = act->lex_env; - act = NULL; /* invalidated */ + act_lex_env = act_caller->lex_env; - (void) duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - act_lex_env); - new_env = duk_require_hobject(ctx, -1); + new_env = duk_hdecenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV)); DUK_ASSERT(new_env != NULL); - DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", - (duk_heaphdr *) new_env)); + duk_push_hobject(thr, (duk_hobject *) new_env); - outer_lex_env = new_env; - outer_var_env = new_env; + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); + DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act_lex_env); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, act_lex_env); + DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); - duk_insert(ctx, 0); /* stash to bottom of value stack to keep new_env reachable for duration of eval */ + outer_lex_env = (duk_hobject *) new_env; + outer_var_env = (duk_hobject *) new_env; + + duk_insert(thr, 0); /* stash to bottom of value stack to keep new_env reachable for duration of eval */ /* compiler's responsibility */ DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); @@ -28147,8 +36161,8 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { "var_env and lex_env to caller's envs, " "this_binding to caller's this_binding")); - outer_lex_env = act->lex_env; - outer_var_env = act->var_env; + outer_lex_env = act_caller->lex_env; + outer_var_env = act_caller->var_env; /* compiler's responsibility */ DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func)); @@ -28161,35 +36175,44 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; } - act = NULL; /* Eval code doesn't need an automatic .prototype object. */ duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 0 /*add_auto_proto*/); - /* [ source template closure ] */ + /* [ env? source template closure ] */ if (this_to_global) { DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL); + duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL); } else { duk_tval *tv; DUK_ASSERT(thr->callstack_top >= 2); - act = thr->callstack + thr->callstack_top + level; /* caller */ - tv = thr->valstack + act->idx_bottom - 1; /* this is just beneath bottom */ + DUK_ASSERT(act_caller != NULL); + tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act_caller->bottom_byteoff - sizeof(duk_tval)); /* this is just beneath bottom */ DUK_ASSERT(tv >= thr->valstack); - duk_push_tval(ctx, tv); + duk_push_tval(thr, tv); } DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T", (duk_heaphdr *) outer_lex_env, (duk_heaphdr *) outer_var_env, - duk_get_tval(ctx, -1))); + duk_get_tval(thr, -1))); - /* [ source template closure this ] */ + /* [ env? source template closure this ] */ - duk_call_method(ctx, 0); + call_flags = 0; + if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) { + /* Set DIRECT_EVAL flag for the call; it's not strictly + * needed for the 'inner' eval call (the eval body) but + * current new.target implementation expects to find it + * so it can traverse direct eval chains up to the real + * calling function. + */ + call_flags |= DUK_CALL_FLAG_DIRECT_EVAL; + } + duk_handle_call_unprotected_nargs(thr, 0, call_flags); - /* [ source template result ] */ + /* [ env? source template result ] */ return 1; } @@ -28198,15 +36221,19 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { * Parsing of ints and floats */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) { +#if defined(DUK_USE_GLOBAL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_hthread *thr) { duk_int32_t radix; duk_small_uint_t s2n_flags; - DUK_ASSERT_TOP(ctx, 2); - duk_to_string(ctx, 0); + DUK_ASSERT_TOP(thr, 2); + duk_to_string(thr, 0); /* Reject symbols. */ - radix = duk_to_int32(ctx, 1); + radix = duk_to_int32(thr, 1); + /* While parseInt() recognizes 0xdeadbeef, it doesn't recognize + * ES2015 0o123 or 0b10001. + */ s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | DUK_S2N_FLAG_ALLOW_GARBAGE | DUK_S2N_FLAG_ALLOW_PLUS | @@ -28232,23 +36259,22 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) { radix = 10; } - duk_dup(ctx, 0); - duk_numconv_parse(ctx, radix, s2n_flags); + duk_dup_0(thr); + duk_numconv_parse(thr, (duk_small_int_t) radix, s2n_flags); return 1; ret_nan: - duk_push_nan(ctx); + duk_push_nan(thr); return 1; } +#endif /* DUK_USE_GLOBAL_BUILTIN */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) { +#if defined(DUK_USE_GLOBAL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_hthread *thr) { duk_small_uint_t s2n_flags; - duk_int32_t radix; - DUK_ASSERT_TOP(ctx, 1); - duk_to_string(ctx, 0); - - radix = 10; + DUK_ASSERT_TOP(thr, 1); + duk_to_string(thr, 0); /* Reject symbols. */ /* XXX: check flags */ s2n_flags = DUK_S2N_FLAG_TRIM_WHITE | @@ -28262,633 +36288,66 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) { DUK_S2N_FLAG_ALLOW_EMPTY_FRAC | DUK_S2N_FLAG_ALLOW_LEADING_ZERO; - duk_numconv_parse(ctx, radix, s2n_flags); + duk_numconv_parse(thr, 10 /*radix*/, s2n_flags); return 1; } +#endif /* DUK_USE_GLOBAL_BUILTIN */ /* * Number checkers */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx) { - duk_double_t d = duk_to_number(ctx, 0); - duk_push_boolean(ctx, DUK_ISNAN(d)); +#if defined(DUK_USE_GLOBAL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_hthread *thr) { + duk_double_t d = duk_to_number(thr, 0); + duk_push_boolean(thr, (duk_bool_t) DUK_ISNAN(d)); return 1; } +#endif /* DUK_USE_GLOBAL_BUILTIN */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx) { - duk_double_t d = duk_to_number(ctx, 0); - duk_push_boolean(ctx, DUK_ISFINITE(d)); +#if defined(DUK_USE_GLOBAL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_hthread *thr) { + duk_double_t d = duk_to_number(thr, 0); + duk_push_boolean(thr, (duk_bool_t) DUK_ISFINITE(d)); return 1; } +#endif /* DUK_USE_GLOBAL_BUILTIN */ /* * URI handling */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table); +#if defined(DUK_USE_GLOBAL_BUILTIN) +DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table); } -DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table); +DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table); } -DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table); +DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table); } -DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table); +DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table); } -#ifdef DUK_USE_SECTION_B -DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_escape, (const void *) NULL); +#if defined(DUK_USE_SECTION_B) +DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_escape, (const void *) NULL); } -DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_unescape, (const void *) NULL); -} -#else /* DUK_USE_SECTION_B */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} - -DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; +DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_hthread *thr) { + return duk__transform_helper(thr, duk__transform_callback_unescape, (const void *) NULL); } #endif /* DUK_USE_SECTION_B */ +#endif /* DUK_USE_GLOBAL_BUILTIN */ -#if defined(DUK_USE_BROWSER_LIKE) && (defined(DUK_USE_FILE_IO) || defined(DUK_USE_DEBUGGER_SUPPORT)) -DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_int_t magic; - duk_idx_t nargs; - const duk_uint8_t *buf; - duk_size_t sz_buf; - const char nl = (const char) DUK_ASC_LF; -#ifndef DUK_USE_PREFER_SIZE - duk_uint8_t buf_stack[256]; -#endif -#ifdef DUK_USE_FILE_IO - duk_file *f_out; -#endif - - DUK_UNREF(thr); - - magic = duk_get_current_magic(ctx); - DUK_UNREF(magic); - - nargs = duk_get_top(ctx); - - /* If argument count is 1 and first argument is a buffer, write the buffer - * as raw data into the file without a newline; this allows exact control - * over stdout/stderr without an additional entrypoint (useful for now). - * - * Otherwise current print/alert semantics are to ToString() coerce - * arguments, join them with a single space, and append a newline. - */ - - if (nargs == 1 && duk_is_buffer(ctx, 0)) { - buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf); - DUK_ASSERT(buf != NULL); - } else if (nargs > 0) { -#ifdef DUK_USE_PREFER_SIZE - /* Compact but lots of churn. */ - duk_push_hstring_stridx(thr, DUK_STRIDX_SPACE); - duk_insert(ctx, 0); - duk_join(ctx, nargs); - duk_push_string(thr, "\n"); - duk_concat(ctx, 2); - buf = (const duk_uint8_t *) duk_get_lstring(ctx, -1, &sz_buf); - DUK_ASSERT(buf != NULL); -#else /* DUK_USE_PREFER_SIZE */ - /* Higher footprint, less churn. */ - duk_idx_t i; - duk_size_t sz_str; - const duk_uint8_t *p_str; - duk_uint8_t *p; - - sz_buf = (duk_size_t) nargs; /* spaces (nargs - 1) + newline */ - for (i = 0; i < nargs; i++) { - (void) duk_to_lstring(ctx, i, &sz_str); - sz_buf += sz_str; - } - - if (sz_buf <= sizeof(buf_stack)) { - p = (duk_uint8_t *) buf_stack; - } else { - p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sz_buf); - DUK_ASSERT(p != NULL); - } - - buf = (const duk_uint8_t *) p; - for (i = 0; i < nargs; i++) { - p_str = (const duk_uint8_t *) duk_get_lstring(ctx, i, &sz_str); - DUK_ASSERT(p_str != NULL); - DUK_MEMCPY((void *) p, (const void *) p_str, sz_str); - p += sz_str; - *p++ = (duk_uint8_t) (i == nargs - 1 ? DUK_ASC_LF : DUK_ASC_SPACE); - } - DUK_ASSERT((const duk_uint8_t *) p == buf + sz_buf); -#endif /* DUK_USE_PREFER_SIZE */ - } else { - buf = (const duk_uint8_t *) &nl; - sz_buf = 1; - } - - /* 'buf' contains the string to write, 'sz_buf' contains the length - * (which may be zero). - */ - DUK_ASSERT(buf != NULL); - - if (sz_buf == 0) { - return 0; - } - -#ifdef DUK_USE_FILE_IO - f_out = (magic ? DUK_STDERR : DUK_STDOUT); - DUK_FWRITE((const void *) buf, 1, (size_t) sz_buf, f_out); - DUK_FFLUSH(f_out); -#endif - -#if defined(DUK_USE_DEBUGGER_SUPPORT) && defined(DUK_USE_DEBUGGER_FWD_PRINTALERT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { - duk_debug_write_notify(thr, magic ? DUK_DBG_CMD_ALERT : DUK_DBG_CMD_PRINT); - duk_debug_write_string(thr, (const char *) buf, sz_buf); - duk_debug_write_eom(thr); - } -#endif - return 0; -} -#elif defined(DUK_USE_BROWSER_LIKE) /* print provider */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { - DUK_UNREF(ctx); - return 0; -} -#else /* print provider */ -DUK_INTERNAL duk_ret_t duk_bi_global_object_print_helper(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} -#endif /* print provider */ - -/* - * CommonJS require() and modules support - */ - -#if defined(DUK_USE_COMMONJS_MODULES) -DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_uint8_t buf[DUK_BI_COMMONJS_MODULE_ID_LIMIT]; - duk_uint8_t *p; - duk_uint8_t *q; - duk_uint8_t *q_last; /* last component */ - duk_int_t int_rc; - - DUK_ASSERT(req_id != NULL); - /* mod_id may be NULL */ - - /* - * A few notes on the algorithm: - * - * - Terms are not allowed to begin with a period unless the term - * is either '.' or '..'. This simplifies implementation (and - * is within CommonJS modules specification). - * - * - There are few output bound checks here. This is on purpose: - * the resolution input is length checked and the output is never - * longer than the input. The resolved output is written directly - * over the input because it's never longer than the input at any - * point in the algorithm. - * - * - Non-ASCII characters are processed as individual bytes and - * need no special treatment. However, U+0000 terminates the - * algorithm; this is not an issue because U+0000 is not a - * desirable term character anyway. - */ - - /* - * Set up the resolution input which is the requested ID directly - * (if absolute or no current module path) or with current module - * ID prepended (if relative and current module path exists). - * - * Suppose current module is 'foo/bar' and relative path is './quux'. - * The 'bar' component must be replaced so the initial input here is - * 'foo/bar/.././quux'. - */ - - if (mod_id != NULL && req_id[0] == '.') { - int_rc = DUK_SNPRINTF((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id); - } else { - int_rc = DUK_SNPRINTF((char *) buf, sizeof(buf), "%s", req_id); - } - if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) { - /* Potentially truncated, NUL not guaranteed in any case. - * The (int_rc < 0) case should not occur in practice. - */ - DUK_DD(DUK_DDPRINT("resolve error: temporary working module ID doesn't fit into resolve buffer")); - goto resolve_error; - } - DUK_ASSERT(DUK_STRLEN((const char *) buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */ - - DUK_DDD(DUK_DDDPRINT("input module id: '%s'", (const char *) buf)); - - /* - * Resolution loop. At the top of the loop we're expecting a valid - * term: '.', '..', or a non-empty identifier not starting with a period. - */ - - p = buf; - q = buf; - for (;;) { - duk_uint_fast8_t c; - - /* Here 'p' always points to the start of a term. - * - * We can also unconditionally reset q_last here: if this is - * the last (non-empty) term q_last will have the right value - * on loop exit. - */ - - DUK_ASSERT(p >= q); /* output is never longer than input during resolution */ - - DUK_DDD(DUK_DDDPRINT("resolve loop top: p -> '%s', q=%p, buf=%p", - (const char *) p, (void *) q, (void *) buf)); - - q_last = q; - - c = *p++; - if (DUK_UNLIKELY(c == 0)) { - DUK_DD(DUK_DDPRINT("resolve error: requested ID must end with a non-empty term")); - goto resolve_error; - } else if (DUK_UNLIKELY(c == '.')) { - c = *p++; - if (c == '/') { - /* Term was '.' and is eaten entirely (including dup slashes). */ - goto eat_dup_slashes; - } - if (c == '.' && *p == '/') { - /* Term was '..', backtrack resolved name by one component. - * q[-1] = previous slash (or beyond start of buffer) - * q[-2] = last char of previous component (or beyond start of buffer) - */ - p++; /* eat (first) input slash */ - DUK_ASSERT(q >= buf); - if (q == buf) { - DUK_DD(DUK_DDPRINT("resolve error: term was '..' but nothing to backtrack")); - goto resolve_error; - } - DUK_ASSERT(*(q - 1) == '/'); - q--; /* backtrack to last output slash (dups already eliminated) */ - for (;;) { - /* Backtrack to previous slash or start of buffer. */ - DUK_ASSERT(q >= buf); - if (q == buf) { - break; - } - if (*(q - 1) == '/') { - break; - } - q--; - } - goto eat_dup_slashes; - } - DUK_DD(DUK_DDPRINT("resolve error: term begins with '.' but is not '.' or '..' (not allowed now)")); - goto resolve_error; - } else if (DUK_UNLIKELY(c == '/')) { - /* e.g. require('/foo'), empty terms not allowed */ - DUK_DD(DUK_DDPRINT("resolve error: empty term (not allowed now)")); - goto resolve_error; - } else { - for (;;) { - /* Copy term name until end or '/'. */ - *q++ = c; - c = *p++; - if (DUK_UNLIKELY(c == 0)) { - /* This was the last term, and q_last was - * updated to match this term at loop top. - */ - goto loop_done; - } else if (DUK_UNLIKELY(c == '/')) { - *q++ = '/'; - break; - } else { - /* write on next loop */ - } - } - } - - eat_dup_slashes: - for (;;) { - /* eat dup slashes */ - c = *p; - if (DUK_LIKELY(c != '/')) { - break; - } - p++; - } - } - loop_done: - /* Output #1: resolved absolute name */ - DUK_ASSERT(q >= buf); - duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf)); - - /* Output #2: last component name */ - DUK_ASSERT(q >= q_last); - DUK_ASSERT(q_last >= buf); - duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last)); - - DUK_DD(DUK_DDPRINT("after resolving module name: buf=%p, q_last=%p, q=%p", - (void *) buf, (void *) q_last, (void *) q)); - return; - - resolve_error: - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "cannot resolve module id: %s", (const char *) req_id); -} -#endif /* DUK_USE_COMMONJS_MODULES */ - -#if defined(DUK_USE_COMMONJS_MODULES) -/* Stack indices for better readability */ -#define DUK__IDX_REQUESTED_ID 0 /* Module id requested */ -#define DUK__IDX_REQUIRE 1 /* Current require() function */ -#define DUK__IDX_REQUIRE_ID 2 /* The base ID of the current require() function, resolution base */ -#define DUK__IDX_RESOLVED_ID 3 /* Resolved, normalized absolute module ID */ -#define DUK__IDX_LASTCOMP 4 /* Last component name in resolved path */ -#define DUK__IDX_DUKTAPE 5 /* Duktape object */ -#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */ -#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */ -#define DUK__IDX_FRESH_REQUIRE 8 /* New require() function for module, updated resolution base */ -#define DUK__IDX_EXPORTS 9 /* Default exports table */ -#define DUK__IDX_MODULE 10 /* Module object containing module.exports, etc */ - -DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { - const char *str_req_id; /* requested identifier */ - const char *str_mod_id; /* require.id of current module */ - duk_int_t pcall_rc; - - /* NOTE: we try to minimize code size by avoiding unnecessary pops, - * so the stack looks a bit cluttered in this function. DUK_ASSERT_TOP() - * assertions are used to ensure stack configuration is correct at each - * step. - */ - - /* - * Resolve module identifier into canonical absolute form. - */ - - str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID); - duk_push_current_function(ctx); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_ID); - str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */ - DUK_DDD(DUK_DDDPRINT("resolve module id: requested=%!T, currentmodule=%!T", - duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), - duk_get_tval(ctx, DUK__IDX_REQUIRE_ID))); - duk__bi_global_resolve_module_id(ctx, str_req_id, str_mod_id); - str_req_id = NULL; - str_mod_id = NULL; - DUK_DDD(DUK_DDDPRINT("resolved module id: requested=%!T, currentmodule=%!T, result=%!T, lastcomp=%!T", - duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), - duk_get_tval(ctx, DUK__IDX_REQUIRE_ID), - duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), - duk_get_tval(ctx, DUK__IDX_LASTCOMP))); - - /* [ requested_id require require.id resolved_id last_comp ] */ - DUK_ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1); - - /* - * Cached module check. - * - * If module has been loaded or its loading has already begun without - * finishing, return the same cached value ('exports'). The value is - * registered when module load starts so that circular references can - * be supported to some extent. - */ - - duk_push_hobject_bidx(ctx, DUK_BIDX_DUKTAPE); - duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_LOADED); /* Duktape.modLoaded */ - (void) duk_require_hobject(ctx, DUK__IDX_MODLOADED); - DUK_ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1); - - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) { - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */ - DUK_DD(DUK_DDPRINT("module already loaded: %!T", - duk_get_tval(ctx, DUK__IDX_RESOLVED_ID))); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_EXPORTS); /* return module.exports */ - return 1; - } - DUK_ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1); - - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */ - - /* - * Module not loaded (and loading not started previously). - * - * Create a new require() function with 'id' set to resolved ID - * of module being loaded. Also create 'exports' and 'module' - * tables but don't register exports to the loaded table yet. - * We don't want to do that unless the user module search callbacks - * succeeds in finding the module. - */ - - DUK_D(DUK_DPRINT("loading module %!T, resolution base %!T, requested ID %!T -> resolved ID %!T, last component %!T", - duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), - duk_get_tval(ctx, DUK__IDX_REQUIRE_ID), - duk_get_tval(ctx, DUK__IDX_REQUESTED_ID), - duk_get_tval(ctx, DUK__IDX_RESOLVED_ID), - duk_get_tval(ctx, DUK__IDX_LASTCOMP))); - - /* Fresh require: require.id is left configurable (but not writable) - * so that is not easy to accidentally tweak it, but it can still be - * done with Object.defineProperty(). - * - * XXX: require.id could also be just made non-configurable, as there - * is no practical reason to touch it. - */ - duk_push_c_function(ctx, duk_bi_global_object_require, 1 /*nargs*/); - duk_push_hstring_stridx(ctx, DUK_STRIDX_REQUIRE); - duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_C); /* a fresh require() with require.id = resolved target module id */ - - /* Module table: - * - module.exports: initial exports table (may be replaced by user) - * - module.id is non-writable and non-configurable, as the CommonJS - * spec suggests this if possible - * - module.filename: not set, defaults to resolved ID if not explicitly - * set by modSearch() (note capitalization, not .fileName, matches Node.js) - * - module.name: not set, defaults to last component of resolved ID if - * not explicitly set by modSearch() - */ - duk_push_object(ctx); /* exports */ - duk_push_object(ctx); /* module */ - duk_dup(ctx, DUK__IDX_EXPORTS); - duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS, DUK_PROPDESC_FLAGS_WC); /* module.exports = exports */ - duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */ - duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_NONE); /* module.id = resolved_id */ - duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */ - DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 1); - - DUK_DD(DUK_DDPRINT("module table created: %!T", duk_get_tval(ctx, DUK__IDX_MODULE))); - - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */ - - /* Register the module table early to modLoaded[] so that we can - * support circular references even in modSearch(). If an error - * is thrown, we'll delete the reference. - */ - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - duk_dup(ctx, DUK__IDX_MODULE); - duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */ - - /* - * Call user provided module search function and build the wrapped - * module source code (if necessary). The module search function - * can be used to implement pure Ecmacsript, pure C, and mixed - * Ecmascript/C modules. - * - * The module search function can operate on the exports table directly - * (e.g. DLL code can register values to it). It can also return a - * string which is interpreted as module source code (if a non-string - * is returned the module is assumed to be a pure C one). If a module - * cannot be found, an error must be thrown by the user callback. - * - * Because Duktape.modLoaded[] already contains the module being - * loaded, circular references for C modules should also work - * (although expected to be quite rare). - */ - - duk_push_string(ctx, "(function(require,exports,module){"); - - /* Duktape.modSearch(resolved_id, fresh_require, exports, module). */ - duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_SEARCH); /* Duktape.modSearch */ - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); - duk_dup(ctx, DUK__IDX_EXPORTS); - duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */ - pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */ - DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 3); - - if (pcall_rc != DUK_EXEC_SUCCESS) { - /* Delete entry in Duktape.modLoaded[] and rethrow. */ - goto delete_rethrow; - } - - /* If user callback did not return source code, module loading - * is finished (user callback initialized exports table directly). - */ - if (!duk_is_string(ctx, -1)) { - /* User callback did not return source code, so module loading - * is finished: just update modLoaded with final module.exports - * and we're done. - */ - goto return_exports; - } - - /* Finish the wrapped module source. Force module.filename as the - * function .fileName so it gets set for functions defined within a - * module. This also ensures loggers created within the module get - * the module ID (or overridden filename) as their default logger name. - * (Note capitalization: .filename matches Node.js while .fileName is - * used elsewhere in Duktape.) - */ - duk_push_string(ctx, "})"); - duk_concat(ctx, 3); - if (!duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_FILENAME)) { - /* module.filename for .fileName, default to resolved ID if - * not present. - */ - duk_pop(ctx); - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - } - duk_eval_raw(ctx, NULL, 0, DUK_COMPILE_EVAL); - - /* Module has now evaluated to a wrapped module function. Force its - * .name to match module.name (defaults to last component of resolved - * ID) so that it is shown in stack traces too. Note that we must not - * introduce an actual name binding into the function scope (which is - * usually the case with a named function) because it would affect the - * scope seen by the module and shadow accesses to globals of the same name. - * This is now done by compiling the function as anonymous and then forcing - * its .name without setting a "has name binding" flag. - */ - - duk_push_hstring_stridx(ctx, DUK_STRIDX_NAME); - if (!duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_NAME)) { - /* module.name for .name, default to last component if - * not present. - */ - duk_pop(ctx); - duk_dup(ctx, DUK__IDX_LASTCOMP); - } - duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); - - /* - * Call the wrapped module function. - * - * Use a protected call so that we can update Duktape.modLoaded[resolved_id] - * even if the module throws an error. - */ - - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */ - DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); - - duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */ - duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */ - duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS); /* relookup exports from module.exports in case it was changed by modSearch */ - duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */ - DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 6); - - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */ - - pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/); - if (pcall_rc != DUK_EXEC_SUCCESS) { - /* Module loading failed. Node.js will forget the module - * registration so that another require() will try to load - * the module again. Mimic that behavior. - */ - goto delete_rethrow; - } - - /* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */ - DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2); - - /* fall through */ - - return_exports: - duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS); - duk_compact(ctx, -1); /* compact the exports table */ - return 1; /* return module.exports */ - - delete_rethrow: - duk_dup(ctx, DUK__IDX_RESOLVED_ID); - duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */ - duk_throw(ctx); /* rethrow original error */ - return 0; /* not reachable */ -} - -#undef DUK__IDX_REQUESTED_ID -#undef DUK__IDX_REQUIRE -#undef DUK__IDX_REQUIRE_ID -#undef DUK__IDX_RESOLVED_ID -#undef DUK__IDX_LASTCOMP -#undef DUK__IDX_DUKTAPE -#undef DUK__IDX_MODLOADED -#undef DUK__IDX_UNDEFINED -#undef DUK__IDX_FRESH_REQUIRE -#undef DUK__IDX_EXPORTS -#undef DUK__IDX_MODULE -#else -DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { - DUK_UNREF(ctx); - return DUK_RET_UNSUPPORTED_ERROR; -} -#endif /* DUK_USE_COMMONJS_MODULES */ -#line 1 "duk_bi_json.c" +/* automatic undefs */ +#undef DUK__CHECK_BITMASK +#undef DUK__MKBITS /* * JSON built-ins. * @@ -28905,7 +36364,9 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { * indeed correct! */ -/* include removed: duk_internal.h */ +/* #include duk_internal.h -> already included */ + +#if defined(DUK_USE_JSON_SUPPORT) /* * Local defines and forward declarations. @@ -28917,26 +36378,28 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) { #define DUK__JSON_STRINGIFY_BUFSIZE 128 #define DUK__JSON_MAX_ESC_LEN 10 /* '\Udeadbeef' */ -DUK_LOCAL_DECL void duk__dec_syntax_error(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_eat_white(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint8_t duk__dec_peek(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint8_t duk__dec_get(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint8_t duk__dec_get_nonwhite(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n); -DUK_LOCAL_DECL void duk__dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx); -DUK_LOCAL_DECL void duk__dec_string(duk_json_dec_ctx *js_ctx); -#ifdef DUK_USE_JX -DUK_LOCAL_DECL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_pointer(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_buffer(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx); +#if defined(DUK_USE_JX) +DUK_LOCAL_DECL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx); #endif -DUK_LOCAL_DECL void duk__dec_number(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_objarr_entry(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_objarr_exit(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_object(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_array(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_value(duk_json_dec_ctx *js_ctx); -DUK_LOCAL_DECL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n); +DUK_LOCAL_DECL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx); +DUK_LOCAL_DECL void duk__json_dec_string(duk_json_dec_ctx *js_ctx); +#if defined(DUK_USE_JX) +DUK_LOCAL_DECL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_buffer(duk_json_dec_ctx *js_ctx); +#endif +DUK_LOCAL_DECL void duk__json_dec_number(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_object(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_array(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_value(duk_json_dec_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx); DUK_LOCAL_DECL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch); DUK_LOCAL_DECL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2); @@ -28947,24 +36410,29 @@ DUK_LOCAL_DECL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *p); #endif DUK_LOCAL_DECL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx); DUK_LOCAL_DECL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uint_fast32_t cp, duk_uint8_t *q); -DUK_LOCAL_DECL void duk__enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k); -DUK_LOCAL_DECL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str); -DUK_LOCAL_DECL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); -DUK_LOCAL_DECL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); -DUK_LOCAL_DECL void duk__enc_object(duk_json_enc_ctx *js_ctx); -DUK_LOCAL_DECL void duk__enc_array(duk_json_enc_ctx *js_ctx); -DUK_LOCAL_DECL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder); -DUK_LOCAL_DECL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv); -DUK_LOCAL_DECL void duk__enc_double(duk_json_enc_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k); +DUK_LOCAL_DECL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str); +DUK_LOCAL_DECL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); +DUK_LOCAL_DECL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top); +DUK_LOCAL_DECL void duk__json_enc_object(duk_json_enc_ctx *js_ctx); +DUK_LOCAL_DECL void duk__json_enc_array(duk_json_enc_ctx *js_ctx); +DUK_LOCAL_DECL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder); +DUK_LOCAL_DECL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv); +DUK_LOCAL_DECL void duk__json_enc_double(duk_json_enc_ctx *js_ctx); #if defined(DUK_USE_FASTINT) -DUK_LOCAL_DECL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv); +DUK_LOCAL_DECL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv); #endif #if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL_DECL void duk__enc_buffer(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); -DUK_LOCAL_DECL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr); -DUK_LOCAL_DECL void duk__enc_bufferobject(duk_json_enc_ctx *js_ctx, duk_hbufferobject *h_bufobj); +DUK_LOCAL_DECL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); +DUK_LOCAL_DECL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr); +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) +DUK_LOCAL_DECL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj); #endif -DUK_LOCAL_DECL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth); +#endif +#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) +DUK_LOCAL_DECL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); +#endif +DUK_LOCAL_DECL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth); /* * Helper tables @@ -29089,16 +36557,17 @@ DUK_LOCAL const duk_uint8_t duk__json_decnumber_lookup[256] = { * CESU-8 encodings. */ -DUK_LOCAL void duk__dec_syntax_error(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL void duk__json_dec_syntax_error(duk_json_dec_ctx *js_ctx) { /* Shared handler to minimize parser size. Cause will be * hidden, unfortunately, but we'll have an offset which * is often quite enough. */ DUK_ERROR_FMT1(js_ctx->thr, DUK_ERR_SYNTAX_ERROR, DUK_STR_FMT_INVALID_JSON, (long) (js_ctx->p - js_ctx->p_start)); + DUK_WO_NORETURN(return;); } -DUK_LOCAL void duk__dec_eat_white(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL void duk__json_dec_eat_white(duk_json_dec_ctx *js_ctx) { const duk_uint8_t *p; duk_uint8_t t; @@ -29128,23 +36597,25 @@ DUK_LOCAL void duk__dec_eat_white(duk_json_dec_ctx *js_ctx) { js_ctx->p = p; } -DUK_LOCAL duk_uint8_t duk__dec_peek(duk_json_dec_ctx *js_ctx) { +#if defined(DUK_USE_JX) +DUK_LOCAL duk_uint8_t duk__json_dec_peek(duk_json_dec_ctx *js_ctx) { DUK_ASSERT(js_ctx->p <= js_ctx->p_end); return *js_ctx->p; } +#endif -DUK_LOCAL duk_uint8_t duk__dec_get(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL duk_uint8_t duk__json_dec_get(duk_json_dec_ctx *js_ctx) { DUK_ASSERT(js_ctx->p <= js_ctx->p_end); return *js_ctx->p++; } -DUK_LOCAL duk_uint8_t duk__dec_get_nonwhite(duk_json_dec_ctx *js_ctx) { - duk__dec_eat_white(js_ctx); - return duk__dec_get(js_ctx); +DUK_LOCAL duk_uint8_t duk__json_dec_get_nonwhite(duk_json_dec_ctx *js_ctx) { + duk__json_dec_eat_white(js_ctx); + return duk__json_dec_get(js_ctx); } /* For JX, expressing the whole unsigned 32-bit range matters. */ -DUK_LOCAL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n) { +DUK_LOCAL duk_uint_fast32_t duk__json_dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, duk_small_uint_t n) { duk_small_uint_t i; duk_uint_fast32_t res = 0; duk_uint8_t x; @@ -29153,7 +36624,7 @@ DUK_LOCAL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, for (i = 0; i < n; i++) { /* XXX: share helper from lexer; duk_lexer.c / hexval(). */ - x = duk__dec_get(js_ctx); + x = duk__json_dec_get(js_ctx); DUK_DDD(DUK_DDDPRINT("decode_hex_escape: i=%ld, n=%ld, res=%ld, x=%ld", (long) i, (long) n, (long) res, (long) x)); @@ -29161,7 +36632,7 @@ DUK_LOCAL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, DUK_ASSERT(duk_hex_dectab[0] == -1); t = duk_hex_dectab[x & 0xff]; if (DUK_LIKELY(t >= 0)) { - res = (res * 16) + t; + res = (res * 16) + (duk_uint_fast32_t) t; } else { /* catches EOF and invalid digits */ goto syntax_error; @@ -29172,12 +36643,12 @@ DUK_LOCAL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, return res; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); return 0; } -DUK_LOCAL void duk__dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx) { +DUK_LOCAL void duk__json_dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t stridx) { duk_hstring *h; const duk_uint8_t *p; duk_uint8_t x, y; @@ -29187,8 +36658,7 @@ DUK_LOCAL void duk__dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t st * have internal NULs. */ - DUK_ASSERT_DISABLE(stridx >= 0); /* unsigned */ - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); + DUK_ASSERT_STRIDX_VALID(stridx); h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx); DUK_ASSERT(h != NULL); @@ -29200,7 +36670,7 @@ DUK_LOCAL void duk__dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t st if (x == 0) { break; } - y = duk__dec_get(js_ctx); + y = duk__json_dec_get(js_ctx); if (x != y) { /* Catches EOF of JSON input. */ goto syntax_error; @@ -29211,19 +36681,19 @@ DUK_LOCAL void duk__dec_req_stridx(duk_json_dec_ctx *js_ctx, duk_small_uint_t st return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } -DUK_LOCAL duk_small_int_t duk__dec_string_escape(duk_json_dec_ctx *js_ctx, duk_uint8_t **ext_p) { +DUK_LOCAL duk_small_int_t duk__json_dec_string_escape(duk_json_dec_ctx *js_ctx, duk_uint8_t **ext_p) { duk_uint_fast32_t cp; /* EOF (-1) will be cast to an unsigned value first * and then re-cast for the switch. In any case, it * will match the default case (syntax error). */ - cp = (duk_uint_fast32_t) duk__dec_get(js_ctx); - switch ((int) cp) { + cp = (duk_uint_fast32_t) duk__json_dec_get(js_ctx); + switch (cp) { case DUK_ASC_BACKSLASH: break; case DUK_ASC_DOUBLEQUOTE: break; case DUK_ASC_SLASH: break; @@ -29233,13 +36703,13 @@ DUK_LOCAL duk_small_int_t duk__dec_string_escape(duk_json_dec_ctx *js_ctx, duk_u case DUK_ASC_LC_F: cp = 0x0c; break; case DUK_ASC_LC_B: cp = 0x08; break; case DUK_ASC_LC_U: { - cp = duk__dec_decode_hex_escape(js_ctx, 4); + cp = duk__json_dec_decode_hex_escape(js_ctx, 4); break; } -#ifdef DUK_USE_JX +#if defined(DUK_USE_JX) case DUK_ASC_UC_U: { if (js_ctx->flag_ext_custom) { - cp = duk__dec_decode_hex_escape(js_ctx, 8); + cp = duk__json_dec_decode_hex_escape(js_ctx, 8); } else { return 1; /* syntax error */ } @@ -29247,7 +36717,7 @@ DUK_LOCAL duk_small_int_t duk__dec_string_escape(duk_json_dec_ctx *js_ctx, duk_u } case DUK_ASC_LC_X: { if (js_ctx->flag_ext_custom) { - cp = duk__dec_decode_hex_escape(js_ctx, 2); + cp = duk__json_dec_decode_hex_escape(js_ctx, 2); } else { return 1; /* syntax error */ } @@ -29259,14 +36729,13 @@ DUK_LOCAL duk_small_int_t duk__dec_string_escape(duk_json_dec_ctx *js_ctx, duk_u return 1; /* syntax error */ } - DUK_RAW_WRITE_XUTF8(*ext_p, cp); + DUK_RAW_WRITEINC_XUTF8(*ext_p, cp); return 0; } -DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL void duk__json_dec_string(duk_json_dec_ctx *js_ctx) { duk_hthread *thr = js_ctx->thr; - duk_context *ctx = (duk_context *) thr; duk_bufwriter_ctx bw_alloc; duk_bufwriter_ctx *bw; duk_uint8_t *q; @@ -29324,7 +36793,7 @@ DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) { * quite slow but it's uncommon). */ js_ctx->p = p; - if (duk__dec_string_escape(js_ctx, &q) != 0) { + if (duk__json_dec_string_escape(js_ctx, &q) != 0) { goto syntax_error; } break; @@ -29341,12 +36810,12 @@ DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) { q = DUK_BW_ENSURE_RAW(js_ctx->thr, bw, DUK_UNICODE_MAX_XUTF8_LENGTH, q); - x = duk__dec_get(js_ctx); + x = duk__json_dec_get(js_ctx); if (x == DUK_ASC_DOUBLEQUOTE) { break; } else if (x == DUK_ASC_BACKSLASH) { - if (duk__dec_string_escape(js_ctx, &q) != 0) { + if (duk__json_dec_string_escape(js_ctx, &q) != 0) { goto syntax_error; } } else if (x < 0x20) { @@ -29359,24 +36828,23 @@ DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) { #endif /* DUK_USE_JSON_DECSTRING_FASTPATH */ DUK_BW_SETPTR_AND_COMPACT(js_ctx->thr, bw, q); - duk_to_string(ctx, -1); + (void) duk_buffer_to_string(thr, -1); /* Safe if input string is safe. */ /* [ ... str ] */ return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } -#ifdef DUK_USE_JX +#if defined(DUK_USE_JX) /* Decode a plain string consisting entirely of identifier characters. * Used to parse plain keys (e.g. "foo: 123"). */ -DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL void duk__json_dec_plain_string(duk_json_dec_ctx *js_ctx) { duk_hthread *thr = js_ctx->thr; - duk_context *ctx = (duk_context *) thr; const duk_uint8_t *p; duk_small_int_t x; @@ -29409,17 +36877,16 @@ DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) { p++; } - duk_push_lstring(ctx, (const char *) js_ctx->p, (duk_size_t) (p - js_ctx->p)); + duk_push_lstring(thr, (const char *) js_ctx->p, (duk_size_t) (p - js_ctx->p)); js_ctx->p = p; /* [ ... str ] */ } #endif /* DUK_USE_JX */ -#ifdef DUK_USE_JX -DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { +#if defined(DUK_USE_JX) +DUK_LOCAL void duk__json_dec_pointer(duk_json_dec_ctx *js_ctx) { duk_hthread *thr = js_ctx->thr; - duk_context *ctx = (duk_context *) thr; const duk_uint8_t *p; duk_small_int_t x; void *voidptr; @@ -29457,7 +36924,7 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { voidptr = NULL; (void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr); - duk_push_pointer(ctx, voidptr); + duk_push_pointer(thr, voidptr); js_ctx->p = p + 1; /* skip ')' */ /* [ ... ptr ] */ @@ -29465,15 +36932,14 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } #endif /* DUK_USE_JX */ -#ifdef DUK_USE_JX -DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) { +#if defined(DUK_USE_JX) +DUK_LOCAL void duk__json_dec_buffer(duk_json_dec_ctx *js_ctx) { duk_hthread *thr = js_ctx->thr; - duk_context *ctx = (duk_context *) thr; const duk_uint8_t *p; duk_uint8_t *buf; duk_size_t src_len; @@ -29510,11 +36976,12 @@ DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) { p++; } + /* XXX: this is not very nice; unnecessary copy is made. */ src_len = (duk_size_t) (p - js_ctx->p); - buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, src_len); + buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_len); DUK_ASSERT(buf != NULL); - DUK_MEMCPY((void *) buf, (const void *) js_ctx->p, src_len); - duk_hex_decode(ctx, -1); + duk_memcpy((void *) buf, (const void *) js_ctx->p, src_len); + duk_hex_decode(thr, -1); js_ctx->p = p + 1; /* skip '|' */ @@ -29523,14 +36990,14 @@ DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) { return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } #endif /* DUK_USE_JX */ /* Parse a number, other than NaN or +/- Infinity */ -DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_dec_number(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; const duk_uint8_t *p_start; const duk_uint8_t *p; duk_uint8_t x; @@ -29575,40 +37042,43 @@ DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) { js_ctx->p = p; DUK_ASSERT(js_ctx->p > p_start); - duk_push_lstring(ctx, (const char *) p_start, (duk_size_t) (p - p_start)); + duk_push_lstring(thr, (const char *) p_start, (duk_size_t) (p - p_start)); s2n_flags = DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_MINUS | /* but don't allow leading plus */ DUK_S2N_FLAG_ALLOW_FRAC; DUK_DDD(DUK_DDDPRINT("parse_number: string before parsing: %!T", - (duk_tval *) duk_get_tval(ctx, -1))); - duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags); - if (duk_is_nan(ctx, -1)) { - duk__dec_syntax_error(js_ctx); + (duk_tval *) duk_get_tval(thr, -1))); + duk_numconv_parse(thr, 10 /*radix*/, s2n_flags); + if (duk_is_nan(thr, -1)) { + duk__json_dec_syntax_error(js_ctx); } - DUK_ASSERT(duk_is_number(ctx, -1)); + DUK_ASSERT(duk_is_number(thr, -1)); DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T", - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -1))); /* [ ... num ] */ } -DUK_LOCAL void duk__dec_objarr_entry(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; - duk_require_stack(ctx, DUK_JSON_DEC_REQSTACK); +DUK_LOCAL void duk__json_dec_objarr_entry(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; + duk_require_stack(thr, DUK_JSON_DEC_REQSTACK); /* c recursion check */ - DUK_ASSERT(js_ctx->recursion_depth >= 0); + duk_native_stack_check(thr); + + DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { - DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_JSONDEC_RECLIMIT); + DUK_ERROR_RANGE(thr, DUK_STR_DEC_RECLIMIT); + DUK_WO_NORETURN(return;); } js_ctx->recursion_depth++; } -DUK_LOCAL void duk__dec_objarr_exit(duk_json_dec_ctx *js_ctx) { +DUK_LOCAL void duk__json_dec_objarr_exit(duk_json_dec_ctx *js_ctx) { /* c recursion check */ DUK_ASSERT(js_ctx->recursion_depth > 0); @@ -29616,32 +37086,32 @@ DUK_LOCAL void duk__dec_objarr_exit(duk_json_dec_ctx *js_ctx) { js_ctx->recursion_depth--; } -DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_dec_object(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_int_t key_count; /* XXX: a "first" flag would suffice */ duk_uint8_t x; DUK_DDD(DUK_DDDPRINT("parse_object")); - duk__dec_objarr_entry(js_ctx); + duk__json_dec_objarr_entry(js_ctx); - duk_push_object(ctx); + duk_push_object(thr); /* Initial '{' has been checked and eaten by caller. */ key_count = 0; for (;;) { - x = duk__dec_get_nonwhite(js_ctx); + x = duk__json_dec_get_nonwhite(js_ctx); DUK_DDD(DUK_DDDPRINT("parse_object: obj=%!T, x=%ld, key_count=%ld", - (duk_tval *) duk_get_tval(ctx, -1), + (duk_tval *) duk_get_tval(thr, -1), (long) x, (long) key_count)); /* handle comma and closing brace */ if (x == DUK_ASC_COMMA && key_count > 0) { /* accept comma, expect new value */ - x = duk__dec_get_nonwhite(js_ctx); + x = duk__json_dec_get_nonwhite(js_ctx); } else if (x == DUK_ASC_RCURLY) { /* eat closing brace */ break; @@ -29658,11 +37128,11 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { /* parse key and value */ if (x == DUK_ASC_DOUBLEQUOTE) { - duk__dec_string(js_ctx); -#ifdef DUK_USE_JX + duk__json_dec_string(js_ctx); +#if defined(DUK_USE_JX) } else if (js_ctx->flag_ext_custom && duk_unicode_is_identifier_start((duk_codepoint_t) x)) { - duk__dec_plain_string(js_ctx); + duk__json_dec_plain_string(js_ctx); #endif } else { goto syntax_error; @@ -29670,16 +37140,16 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { /* [ ... obj key ] */ - x = duk__dec_get_nonwhite(js_ctx); + x = duk__json_dec_get_nonwhite(js_ctx); if (x != DUK_ASC_COLON) { goto syntax_error; } - duk__dec_value(js_ctx); + duk__json_dec_value(js_ctx); /* [ ... obj key val ] */ - duk_xdef_prop_wec(ctx, -3); + duk_xdef_prop_wec(thr, -3); /* [ ... obj ] */ @@ -29689,35 +37159,35 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { /* [ ... obj ] */ DUK_DDD(DUK_DDDPRINT("parse_object: final object is %!T", - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -1))); - duk__dec_objarr_exit(js_ctx); + duk__json_dec_objarr_exit(js_ctx); return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } -DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_dec_array(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_uarridx_t arr_idx; duk_uint8_t x; DUK_DDD(DUK_DDDPRINT("parse_array")); - duk__dec_objarr_entry(js_ctx); + duk__json_dec_objarr_entry(js_ctx); - duk_push_array(ctx); + duk_push_array(thr); /* Initial '[' has been checked and eaten by caller. */ arr_idx = 0; for (;;) { - x = duk__dec_get_nonwhite(js_ctx); + x = duk__json_dec_get_nonwhite(js_ctx); DUK_DDD(DUK_DDDPRINT("parse_array: arr=%!T, x=%ld, arr_idx=%ld", - (duk_tval *) duk_get_tval(ctx, -1), + (duk_tval *) duk_get_tval(thr, -1), (long) x, (long) arr_idx)); /* handle comma and closing bracket */ @@ -29730,7 +37200,7 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { break; } else if (arr_idx == 0) { /* accept anything, expect first value (EOF will be - * caught by duk__dec_value() below. + * caught by duk__json_dec_value() below. */ js_ctx->p--; /* backtrack (safe) */ } else { @@ -29740,11 +37210,11 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { /* parse value */ - duk__dec_value(js_ctx); + duk__json_dec_value(js_ctx); /* [ ... arr val ] */ - duk_xdef_prop_index_wec(ctx, -2, arr_idx); + duk_xdef_prop_index_wec(thr, -2, arr_idx); arr_idx++; } @@ -29752,155 +37222,156 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { * set the values. */ - duk_set_length(ctx, -1, arr_idx); + duk_set_length(thr, -1, arr_idx); /* [ ... arr ] */ DUK_DDD(DUK_DDDPRINT("parse_array: final array is %!T", - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -1))); - duk__dec_objarr_exit(js_ctx); + duk__json_dec_objarr_exit(js_ctx); return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } -DUK_LOCAL void duk__dec_value(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_dec_value(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_uint8_t x; - x = duk__dec_get_nonwhite(js_ctx); + x = duk__json_dec_get_nonwhite(js_ctx); DUK_DDD(DUK_DDDPRINT("parse_value: initial x=%ld", (long) x)); - /* Note: duk__dec_req_stridx() backtracks one char */ + /* Note: duk__json_dec_req_stridx() backtracks one char */ if (x == DUK_ASC_DOUBLEQUOTE) { - duk__dec_string(js_ctx); + duk__json_dec_string(js_ctx); } else if ((x >= DUK_ASC_0 && x <= DUK_ASC_9) || (x == DUK_ASC_MINUS)) { -#ifdef DUK_USE_JX - if (js_ctx->flag_ext_custom && x == DUK_ASC_MINUS && duk__dec_peek(js_ctx) == DUK_ASC_UC_I) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_MINUS_INFINITY); /* "-Infinity", '-' has been eaten */ - duk_push_number(ctx, -DUK_DOUBLE_INFINITY); +#if defined(DUK_USE_JX) + if (js_ctx->flag_ext_custom && x == DUK_ASC_MINUS && duk__json_dec_peek(js_ctx) == DUK_ASC_UC_I) { + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_MINUS_INFINITY); /* "-Infinity", '-' has been eaten */ + duk_push_number(thr, -DUK_DOUBLE_INFINITY); } else { #else { /* unconditional block */ #endif /* We already ate 'x', so backup one byte. */ js_ctx->p--; /* safe */ - duk__dec_number(js_ctx); + duk__json_dec_number(js_ctx); } } else if (x == DUK_ASC_LC_T) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_TRUE); - duk_push_true(ctx); + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_TRUE); + duk_push_true(thr); } else if (x == DUK_ASC_LC_F) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_FALSE); - duk_push_false(ctx); + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_FALSE); + duk_push_false(thr); } else if (x == DUK_ASC_LC_N) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_LC_NULL); - duk_push_null(ctx); -#ifdef DUK_USE_JX + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_NULL); + duk_push_null(thr); +#if defined(DUK_USE_JX) } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LC_U) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_LC_UNDEFINED); - duk_push_undefined(ctx); + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_LC_UNDEFINED); + duk_push_undefined(thr); } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_N) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_NAN); - duk_push_nan(ctx); + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_NAN); + duk_push_nan(thr); } else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_I) { - duk__dec_req_stridx(js_ctx, DUK_STRIDX_INFINITY); - duk_push_number(ctx, DUK_DOUBLE_INFINITY); + duk__json_dec_req_stridx(js_ctx, DUK_STRIDX_INFINITY); + duk_push_number(thr, DUK_DOUBLE_INFINITY); } else if (js_ctx->flag_ext_custom && x == DUK_ASC_LPAREN) { - duk__dec_pointer(js_ctx); + duk__json_dec_pointer(js_ctx); } else if (js_ctx->flag_ext_custom && x == DUK_ASC_PIPE) { - duk__dec_buffer(js_ctx); + duk__json_dec_buffer(js_ctx); #endif } else if (x == DUK_ASC_LCURLY) { - duk__dec_object(js_ctx); + duk__json_dec_object(js_ctx); } else if (x == DUK_ASC_LBRACKET) { - duk__dec_array(js_ctx); + duk__json_dec_array(js_ctx); } else { /* catches EOF (NUL) */ goto syntax_error; } - duk__dec_eat_white(js_ctx); + duk__json_dec_eat_white(js_ctx); /* [ ... val ] */ return; syntax_error: - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); DUK_UNREACHABLE(); } -/* Recursive value reviver, implements the Walk() algorithm. No C recursion - * check is done here because the initial parsing step will already ensure - * there is a reasonable limit on C recursion depth and hence object depth. +/* Recursive value reviver, implements the Walk() algorithm. The parsing + * step ensures there is a reasonable depth limit to the input. However, + * the reviver may create more depth by editing object or array entries, so + * we have both C recursion limit and native stack checks here. */ -DUK_LOCAL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_dec_reviver_walk(duk_json_dec_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_hobject *h; duk_uarridx_t i, arr_len; + duk__json_dec_objarr_entry(js_ctx); + DUK_DDD(DUK_DDDPRINT("walk: top=%ld, holder=%!T, name=%!T", - (long) duk_get_top(ctx), - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); + (long) duk_get_top(thr), + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); - duk_dup_top(ctx); - duk_get_prop(ctx, -3); /* -> [ ... holder name val ] */ + duk_dup_top(thr); + duk_get_prop(thr, -3); /* -> [ ... holder name val ] */ - h = duk_get_hobject(ctx, -1); + h = duk_get_hobject(thr, -1); if (h != NULL) { - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY) { - arr_len = (duk_uarridx_t) duk_get_length(ctx, -1); + if (duk_js_isarray_hobject(h)) { + arr_len = (duk_uarridx_t) duk_get_length(thr, -1); for (i = 0; i < arr_len; i++) { /* [ ... holder name val ] */ DUK_DDD(DUK_DDDPRINT("walk: array, top=%ld, i=%ld, arr_len=%ld, holder=%!T, name=%!T, val=%!T", - (long) duk_get_top(ctx), (long) i, (long) arr_len, - (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); + (long) duk_get_top(thr), (long) i, (long) arr_len, + (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); - /* XXX: push_uint_string / push_u32_string */ - duk_dup_top(ctx); - duk_push_uint(ctx, (duk_uint_t) i); - duk_to_string(ctx, -1); /* -> [ ... holder name val val ToString(i) ] */ - duk__dec_reviver_walk(js_ctx); /* -> [ ... holder name val new_elem ] */ + duk_dup_top(thr); + (void) duk_push_uint_to_hstring(thr, (duk_uint_t) i); /* -> [ ... holder name val val ToString(i) ] */ + duk__json_dec_reviver_walk(js_ctx); /* -> [ ... holder name val new_elem ] */ - if (duk_is_undefined(ctx, -1)) { - duk_pop(ctx); - duk_del_prop_index(ctx, -1, i); + if (duk_is_undefined(thr, -1)) { + duk_pop(thr); + duk_del_prop_index(thr, -1, i); } else { /* XXX: duk_xdef_prop_index_wec() would be more appropriate * here but it currently makes some assumptions that might * not hold (e.g. that previous property is not an accessor). */ - duk_put_prop_index(ctx, -2, i); + duk_put_prop_index(thr, -2, i); } } } else { /* [ ... holder name val ] */ - duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); - while (duk_next(ctx, -1 /*enum_index*/, 0 /*get_value*/)) { + duk_enum(thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); + while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) { DUK_DDD(DUK_DDDPRINT("walk: object, top=%ld, holder=%!T, name=%!T, val=%!T, enum=%!iT, obj_key=%!T", - (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, -5), - (duk_tval *) duk_get_tval(ctx, -4), (duk_tval *) duk_get_tval(ctx, -3), - (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); + (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -5), + (duk_tval *) duk_get_tval(thr, -4), (duk_tval *) duk_get_tval(thr, -3), + (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1))); /* [ ... holder name val enum obj_key ] */ - duk_dup(ctx, -3); - duk_dup(ctx, -2); + duk_dup_m3(thr); + duk_dup_m2(thr); /* [ ... holder name val enum obj_key val obj_key ] */ - duk__dec_reviver_walk(js_ctx); + duk__json_dec_reviver_walk(js_ctx); /* [ ... holder name val enum obj_key new_elem ] */ - if (duk_is_undefined(ctx, -1)) { - duk_pop(ctx); - duk_del_prop(ctx, -3); + if (duk_is_undefined(thr, -1)) { + duk_pop(thr); + duk_del_prop(thr, -3); } else { /* XXX: duk_xdef_prop_index_wec() would be more appropriate * here but it currently makes some assumptions that might @@ -29911,21 +37382,23 @@ DUK_LOCAL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx) { * does not happen normally, but a clever reviver can trigger * that, see complex reviver case in: test-bug-json-parse-__proto__.js. */ - duk_put_prop(ctx, -4); + duk_put_prop(thr, -4); } } - duk_pop(ctx); /* pop enum */ + duk_pop(thr); /* pop enum */ } } /* [ ... holder name val ] */ - duk_dup(ctx, js_ctx->idx_reviver); - duk_insert(ctx, -4); /* -> [ ... reviver holder name val ] */ - duk_call_method(ctx, 2); /* -> [ ... res ] */ + duk_dup(thr, js_ctx->idx_reviver); + duk_insert(thr, -4); /* -> [ ... reviver holder name val ] */ + duk_call_method(thr, 2); /* -> [ ... res ] */ + + duk__json_dec_objarr_exit(js_ctx); DUK_DDD(DUK_DDDPRINT("walk: top=%ld, result=%!T", - (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, -1))); + (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -1))); } /* @@ -29962,8 +37435,7 @@ DUK_LOCAL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *str) { DUK_LOCAL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t stridx) { duk_hstring *h; - DUK_ASSERT_DISABLE(stridx >= 0); /* unsigned */ - DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); + DUK_ASSERT_STRIDX_VALID(stridx); h = DUK_HTHREAD_GET_STRING(js_ctx->thr, stridx); DUK_ASSERT(h != NULL); @@ -29994,9 +37466,9 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin * (nybble_count << 16) | (escape_char1) | (escape_char2) */ -#ifdef DUK_USE_JX +#if defined(DUK_USE_JX) if (DUK_LIKELY(cp < 0x100UL)) { - if (DUK_UNLIKELY(js_ctx->flag_ext_custom)) { + if (DUK_UNLIKELY(js_ctx->flag_ext_custom != 0U)) { tmp = DUK__MKESC(2, DUK_ASC_BACKSLASH, DUK_ASC_LC_X); } else { tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U); @@ -30006,8 +37478,8 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin if (DUK_LIKELY(cp < 0x10000UL)) { tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U); } else { -#ifdef DUK_USE_JX - if (DUK_LIKELY(js_ctx->flag_ext_custom)) { +#if defined(DUK_USE_JX) + if (DUK_LIKELY(js_ctx->flag_ext_custom != 0U)) { tmp = DUK__MKESC(8, DUK_ASC_BACKSLASH, DUK_ASC_UC_U); } else #endif @@ -30034,7 +37506,7 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin return q; } -DUK_LOCAL void duk__enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) { +DUK_LOCAL void duk__json_enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) { const duk_int8_t *p, *p_start, *p_end; /* Note: intentionally signed. */ duk_size_t k_len; duk_codepoint_t cp; @@ -30077,7 +37549,7 @@ DUK_LOCAL void duk__enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) } quote_normally: - duk__enc_quote_string(js_ctx, k); + duk__json_enc_quote_string(js_ctx, k); } /* The Quote(value) operation: quote a string. @@ -30085,13 +37557,13 @@ DUK_LOCAL void duk__enc_key_autoquote(duk_json_enc_ctx *js_ctx, duk_hstring *k) * Stack policy: [ ] -> [ ]. */ -DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str) { +DUK_LOCAL void duk__json_enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_str) { duk_hthread *thr = js_ctx->thr; const duk_uint8_t *p, *p_start, *p_end, *p_now, *p_tmp; duk_uint8_t *q; duk_ucodepoint_t cp; /* typed for duk_unicode_decode_xutf8() */ - DUK_DDD(DUK_DDDPRINT("duk__enc_quote_string: h_str=%!O", (duk_heaphdr *) h_str)); + DUK_DDD(DUK_DDDPRINT("duk__json_enc_quote_string: h_str=%!O", (duk_heaphdr *) h_str)); DUK_ASSERT(h_str != NULL); p_start = DUK_HSTRING_GET_DATA(h_str); @@ -30183,9 +37655,9 @@ DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st /* If XUTF-8 decoding fails, treat the offending byte as a codepoint directly * and go forward one byte. This is of course very lossy, but allows some kind * of output to be produced even for internal strings which don't conform to - * XUTF-8. All standard Ecmascript strings are always CESU-8, so this behavior - * does not violate the Ecmascript specification. The behavior is applied to - * all modes, including Ecmascript standard JSON. Because the current XUTF-8 + * XUTF-8. All standard ECMAScript strings are always CESU-8, so this behavior + * does not violate the ECMAScript specification. The behavior is applied to + * all modes, including ECMAScript standard JSON. Because the current XUTF-8 * decoding is not very strict, this behavior only really affects initial bytes * and truncated codepoints. * @@ -30200,7 +37672,7 @@ DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st p = p_tmp + 1; } -#ifdef DUK_USE_NONSTD_JSON_ESC_U2028_U2029 +#if defined(DUK_USE_NONSTD_JSON_ESC_U2028_U2029) if (js_ctx->flag_ascii_only || cp == 0x2028 || cp == 0x2029) { #else if (js_ctx->flag_ascii_only) { @@ -30208,7 +37680,7 @@ DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st q = duk__emit_esc_auto_fast(js_ctx, cp, q); } else { /* as is */ - DUK_RAW_WRITE_XUTF8(q, cp); + DUK_RAW_WRITEINC_XUTF8(q, cp); } } } @@ -30222,9 +37694,8 @@ DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st /* Encode a double (checked by caller) from stack top. Stack top may be * replaced by serialized string but is not popped (caller does that). */ -DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { +DUK_LOCAL void duk__json_enc_double(duk_json_enc_ctx *js_ctx) { duk_hthread *thr; - duk_context *ctx; duk_tval *tv; duk_double_t d; duk_small_int_t c; @@ -30236,10 +37707,9 @@ DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { DUK_ASSERT(js_ctx != NULL); thr = js_ctx->thr; DUK_ASSERT(thr != NULL); - ctx = (duk_context *) thr; /* Caller must ensure 'tv' is indeed a double and not a fastint! */ - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv)); d = DUK_TVAL_GET_DOUBLE(tv); @@ -30256,16 +37726,15 @@ DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { */ if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 && (js_ctx->flag_ext_custom_or_compatible))) { - duk_push_hstring_stridx(ctx, DUK_STRIDX_MINUS_ZERO); /* '-0' */ + duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_ZERO); /* '-0' */ } else #endif /* DUK_USE_JX || DUK_USE_JC */ { n2s_flags = 0; /* [ ... number ] -> [ ... string ] */ - duk_numconv_stringify(ctx, 10 /*radix*/, 0 /*digits*/, n2s_flags); + duk_numconv_stringify(thr, 10 /*radix*/, 0 /*digits*/, n2s_flags); } - h_str = duk_to_hstring(ctx, -1); - DUK_ASSERT(h_str != NULL); + h_str = duk_known_hstring(thr, -1); DUK__EMIT_HSTR(js_ctx, h_str); return; } @@ -30289,7 +37758,7 @@ DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { #if defined(DUK_USE_FASTINT) /* Encode a fastint from duk_tval ptr, no value stack effects. */ -DUK_LOCAL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) { +DUK_LOCAL void duk__json_enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) { duk_int64_t v; /* Fastint range is signed 48-bit so longest value is -2^47 = -140737488355328 @@ -30314,7 +37783,7 @@ DUK_LOCAL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) #if defined(DUK_USE_HEX_FASTPATH) -DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { +DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { duk_uint8_t *q; duk_uint16_t *q16; duk_small_uint_t x; @@ -30325,7 +37794,7 @@ DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size /* Unlike in duk_hex_encode() 'dst' is not necessarily aligned by 2. * For platforms where unaligned accesses are not allowed, shift 'dst' - * ahead by 1 byte to get alignment and then DUK_MEMMOVE() the result + * ahead by 1 byte to get alignment and then duk_memmove() the result * in place. The faster encoding loop makes up the difference. * There's always space for one extra byte because a terminator always * follows the hex data and that's been accounted for by the caller. @@ -30358,7 +37827,7 @@ DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size #if !defined(DUK_USE_UNALIGNED_ACCESSES_POSSIBLE) if (shift_dst) { q--; - DUK_MEMMOVE((void *) dst, (const void *) (dst + 1), 2 * len_safe); + duk_memmove((void *) dst, (const void *) (dst + 1), 2 * len_safe); DUK_ASSERT(dst + 2 * len_safe == q); } #endif @@ -30372,7 +37841,7 @@ DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size return q; } #else /* DUK_USE_HEX_FASTPATH */ -DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { +DUK_LOCAL duk_uint8_t *duk__json_enc_buffer_data_hex(const duk_uint8_t *src, duk_size_t src_len, duk_uint8_t *dst) { const duk_uint8_t *p; const duk_uint8_t *p_end; duk_uint8_t *q; @@ -30391,7 +37860,7 @@ DUK_LOCAL duk_uint8_t *duk__enc_buffer_data_hex(const duk_uint8_t *src, duk_size } #endif /* DUK_USE_HEX_FASTPATH */ -DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_data, duk_size_t buf_len) { +DUK_LOCAL void duk__json_enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_data, duk_size_t buf_len) { duk_hthread *thr; duk_uint8_t *q; duk_size_t space; @@ -30407,8 +37876,8 @@ DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_d * variants is to be as useful to a programmer as possible. */ - /* The #ifdef clutter here needs to handle the three cases: - * (1) JX+JC, (2) JX only, (3) JC only. + /* The #if defined() clutter here needs to handle the three + * cases: (1) JX+JC, (2) JX only, (3) JC only. */ /* Note: space must cater for both JX and JC. */ @@ -30423,7 +37892,7 @@ DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_d #if defined(DUK_USE_JX) { *q++ = DUK_ASC_PIPE; - q = duk__enc_buffer_data_hex(buf_data, buf_len, q); + q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q); *q++ = DUK_ASC_PIPE; } @@ -30434,9 +37903,9 @@ DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_d #if defined(DUK_USE_JC) { DUK_ASSERT(js_ctx->flag_ext_compatible); - DUK_MEMCPY((void *) q, (const void *) "{\"_buf\":\"", 9); /* len: 9 */ + duk_memcpy((void *) q, (const void *) "{\"_buf\":\"", 9); /* len: 9 */ q += 9; - q = duk__enc_buffer_data_hex(buf_data, buf_len, q); + q = duk__json_enc_buffer_data_hex(buf_data, buf_len, q); *q++ = DUK_ASC_DOUBLEQUOTE; *q++ = DUK_ASC_RCURLY; } @@ -30445,25 +37914,72 @@ DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_d DUK_BW_SET_PTR(thr, &js_ctx->bw, q); } -DUK_LOCAL void duk__enc_buffer(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { - duk__enc_buffer_data(js_ctx, +DUK_LOCAL void duk__json_enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { + duk__json_enc_buffer_data(js_ctx, (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); } #endif /* DUK_USE_JX || DUK_USE_JC */ +#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) +DUK_LOCAL void duk__json_enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { + duk_size_t i, n; + const duk_uint8_t *buf; + duk_uint8_t *q; + + n = DUK_HBUFFER_GET_SIZE(h); + if (n == 0) { + DUK__EMIT_2(js_ctx, DUK_ASC_LCURLY, DUK_ASC_RCURLY); + return; + } + + DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY); + + /* Maximum encoded length with 32-bit index: 1 + 10 + 2 + 3 + 1 + 1 = 18, + * with 64-bit index: 1 + 20 + 2 + 3 + 1 + 1 = 28. 32 has some slack. + * + * Note that because the output buffer is reallocated from time to time, + * side effects (such as finalizers) affecting the buffer 'h' must be + * disabled. This is the case in the JSON.stringify() fast path. + */ + + buf = (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h); + if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { + for (i = 0; i < n; i++) { + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth + 1); + q = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, 32); + q += DUK_SPRINTF((char *) q, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]); + DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); + } + } else { + q = DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw); + for (i = 0; i < n; i++) { + q = DUK_BW_ENSURE_RAW(js_ctx->thr, &js_ctx->bw, 32, q); + q += DUK_SPRINTF((char *) q, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]); + } + DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); + } + DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ + + if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); + } + DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); +} +#endif /* DUK_USE_JSON_STRINGIFY_FASTPATH */ + #if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { +DUK_LOCAL void duk__json_enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { char buf[64]; /* XXX: how to figure correct size? */ const char *fmt; DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); /* caller checks */ DUK_ASSERT(js_ctx->flag_ext_custom_or_compatible); - DUK_MEMZERO(buf, sizeof(buf)); + duk_memzero(buf, sizeof(buf)); - /* The #ifdef clutter here needs to handle the three cases: - * (1) JX+JC, (2) JX only, (3) JC only. + /* The #if defined() clutter here needs to handle the three + * cases: (1) JX+JC, (2) JX only, (3) JC only. */ #if defined(DUK_USE_JX) && defined(DUK_USE_JC) if (js_ctx->flag_ext_custom) @@ -30489,26 +38005,28 @@ DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { } #endif /* DUK_USE_JX || DUK_USE_JC */ +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) #if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL void duk__enc_bufferobject(duk_json_enc_ctx *js_ctx, duk_hbufferobject *h_bufobj) { - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); +DUK_LOCAL void duk__json_enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj) { + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); - if (h_bufobj->buf == NULL || !DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) { + if (h_bufobj->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_bufobj)) { DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); } else { /* Handle both full and partial slice (as long as covered). */ - duk__enc_buffer_data(js_ctx, - (duk_uint8_t *) DUK_HBUFFEROBJECT_GET_SLICE_BASE(js_ctx->thr->heap, h_bufobj), + duk__json_enc_buffer_data(js_ctx, + (duk_uint8_t *) DUK_HBUFOBJ_GET_SLICE_BASE(js_ctx->thr->heap, h_bufobj), (duk_size_t) h_bufobj->length); } } #endif /* DUK_USE_JX || DUK_USE_JC */ +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ /* Indent helper. Calling code relies on js_ctx->recursion_depth also being * directly related to indent depth. */ #if defined(DUK_USE_PREFER_SIZE) -DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth) { +DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) { DUK_ASSERT(js_ctx->h_gap != NULL); DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0); /* caller guarantees */ @@ -30518,7 +38036,7 @@ DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth } } #else /* DUK_USE_PREFER_SIZE */ -DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth) { +DUK_LOCAL void duk__json_enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) { const duk_uint8_t *gap_data; duk_size_t gap_len; duk_size_t avail_bytes; /* bytes of indent available for copying */ @@ -30548,21 +38066,21 @@ DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth p = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, need_bytes); p_start = p; - DUK_MEMCPY((void *) p, (const void *) gap_data, (size_t) gap_len); + duk_memcpy((void *) p, (const void *) gap_data, (size_t) gap_len); p += gap_len; avail_bytes = gap_len; DUK_ASSERT(need_bytes >= gap_len); need_bytes -= gap_len; while (need_bytes >= avail_bytes) { - DUK_MEMCPY((void *) p, (const void *) p_start, (size_t) avail_bytes); + duk_memcpy((void *) p, (const void *) p_start, (size_t) avail_bytes); p += avail_bytes; need_bytes -= avail_bytes; avail_bytes <<= 1; } DUK_ASSERT(need_bytes < avail_bytes); /* need_bytes may be zero */ - DUK_MEMCPY((void *) p, (const void *) p_start, (size_t) need_bytes); + duk_memcpy((void *) p, (const void *) p_start, (size_t) need_bytes); p += need_bytes; /*avail_bytes += need_bytes*/ @@ -30571,21 +38089,21 @@ DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth #endif /* DUK_USE_PREFER_SIZE */ /* Shared entry handling for object/array serialization. */ -DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { + duk_hthread *thr = js_ctx->thr; duk_hobject *h_target; duk_uint_fast32_t i, n; - *entry_top = duk_get_top(ctx); + *entry_top = duk_get_top(thr); - duk_require_stack(ctx, DUK_JSON_ENC_REQSTACK); + duk_native_stack_check(thr); + duk_require_stack(thr, DUK_JSON_ENC_REQSTACK); /* Loop check using a hybrid approach: a fixed-size visited[] array * with overflow in a loop check object. */ - h_target = duk_get_hobject(ctx, -1); /* object or array */ - DUK_ASSERT(h_target != NULL); + h_target = duk_known_hobject(thr, -1); /* object or array */ n = js_ctx->recursion_depth; if (DUK_UNLIKELY(n > DUK_JSON_ENC_LOOPARRAY)) { @@ -30594,37 +38112,40 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_ for (i = 0; i < n; i++) { if (DUK_UNLIKELY(js_ctx->visiting[i] == h_target)) { DUK_DD(DUK_DDPRINT("slow path loop detect")); - DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CYCLIC_INPUT); + DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT); + DUK_WO_NORETURN(return;); } } if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { js_ctx->visiting[js_ctx->recursion_depth] = h_target; } else { - duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target); - duk_dup_top(ctx); /* -> [ ... voidp voidp ] */ - if (duk_has_prop(ctx, js_ctx->idx_loop)) { - DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CYCLIC_INPUT); + duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); + duk_dup_top(thr); /* -> [ ... voidp voidp ] */ + if (duk_has_prop(thr, js_ctx->idx_loop)) { + DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT); + DUK_WO_NORETURN(return;); } - duk_push_true(ctx); /* -> [ ... voidp true ] */ - duk_put_prop(ctx, js_ctx->idx_loop); /* -> [ ... ] */ + duk_push_true(thr); /* -> [ ... voidp true ] */ + duk_put_prop(thr, js_ctx->idx_loop); /* -> [ ... ] */ } /* C recursion check. */ - DUK_ASSERT(js_ctx->recursion_depth >= 0); + DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { - DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_JSONENC_RECLIMIT); + DUK_ERROR_RANGE(thr, DUK_STR_ENC_RECLIMIT); + DUK_WO_NORETURN(return;); } js_ctx->recursion_depth++; DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T", - (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop))); + (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop))); } /* Shared exit handling for object/array serialization. */ -DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) { + duk_hthread *thr = js_ctx->thr; duk_hobject *h_target; /* C recursion check. */ @@ -30635,29 +38156,28 @@ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_t /* Loop check. */ - h_target = duk_get_hobject(ctx, *entry_top - 1); /* original target at entry_top - 1 */ - DUK_ASSERT(h_target != NULL); + h_target = duk_known_hobject(thr, *entry_top - 1); /* original target at entry_top - 1 */ if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) { /* Previous entry was inside visited[], nothing to do. */ } else { - duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target); - duk_del_prop(ctx, js_ctx->idx_loop); /* -> [ ... ] */ + duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target); + duk_del_prop(thr, js_ctx->idx_loop); /* -> [ ... ] */ } /* Restore stack top after unbalanced code paths. */ - duk_set_top(ctx, *entry_top); + duk_set_top(thr, *entry_top); DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T", - (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop))); + (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop))); } /* The JO(value) operation: encode object. * * Stack policy: [ object ] -> [ object ]. */ -DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_enc_object(duk_json_enc_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_hstring *h_key; duk_idx_t entry_top; duk_idx_t idx_obj; @@ -30666,9 +38186,9 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { duk_uarridx_t arr_len, i; duk_size_t prev_size; - DUK_DDD(DUK_DDDPRINT("duk__enc_object: obj=%!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("duk__json_enc_object: obj=%!T", (duk_tval *) duk_get_tval(thr, -1))); - duk__enc_objarr_entry(js_ctx, &entry_top); + duk__json_enc_objarr_entry(js_ctx, &entry_top); idx_obj = entry_top - 1; @@ -30676,14 +38196,14 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { idx_keys = js_ctx->idx_proplist; } else { /* XXX: would be nice to enumerate an object at specified index */ - duk_dup(ctx, idx_obj); - (void) duk_hobject_get_enumerated_keys(ctx, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); /* [ ... target ] -> [ ... target keys ] */ - idx_keys = duk_require_normalize_index(ctx, -1); + duk_dup(thr, idx_obj); + (void) duk_hobject_get_enumerated_keys(thr, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/); /* [ ... target ] -> [ ... target keys ] */ + idx_keys = duk_require_normalize_index(thr, -1); /* leave stack unbalanced on purpose */ } DUK_DDD(DUK_DDDPRINT("idx_keys=%ld, h_keys=%!T", - (long) idx_keys, (duk_tval *) duk_get_tval(ctx, idx_keys))); + (long) idx_keys, (duk_tval *) duk_get_tval(thr, idx_keys))); /* Steps 8-10 have been merged to avoid a "partial" variable. */ @@ -30695,31 +38215,32 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { * that it can be reallocated). */ - arr_len = (duk_uarridx_t) duk_get_length(ctx, idx_keys); + arr_len = (duk_uarridx_t) duk_get_length(thr, idx_keys); emitted = 0; for (i = 0; i < arr_len; i++) { - duk_get_prop_index(ctx, idx_keys, i); /* -> [ ... key ] */ + duk_get_prop_index(thr, idx_keys, i); /* -> [ ... key ] */ DUK_DDD(DUK_DDDPRINT("object property loop: holder=%!T, key=%!T", - (duk_tval *) duk_get_tval(ctx, idx_obj), - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, idx_obj), + (duk_tval *) duk_get_tval(thr, -1))); - h_key = duk_get_hstring(ctx, -1); + h_key = duk_known_hstring(thr, -1); DUK_ASSERT(h_key != NULL); + DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(h_key)); /* proplist filtering; enum options */ prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw); if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth); - duk__enc_key_autoquote(js_ctx, h_key); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); + duk__json_enc_key_autoquote(js_ctx, h_key); DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE); } else { - duk__enc_key_autoquote(js_ctx, h_key); + duk__json_enc_key_autoquote(js_ctx, h_key); DUK__EMIT_1(js_ctx, DUK_ASC_COLON); } /* [ ... key ] */ - if (DUK_UNLIKELY(duk__enc_value(js_ctx, idx_obj) == 0)) { + if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_obj) == 0)) { /* Value would yield 'undefined', so skip key altogether. * Side effects have already happened. */ @@ -30737,31 +38258,31 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); } } DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); - duk__enc_objarr_exit(js_ctx, &entry_top); + duk__json_enc_objarr_exit(js_ctx, &entry_top); - DUK_ASSERT_TOP(ctx, entry_top); + DUK_ASSERT_TOP(thr, entry_top); } /* The JA(value) operation: encode array. * * Stack policy: [ array ] -> [ array ]. */ -DUK_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { - duk_context *ctx = (duk_context *) js_ctx->thr; +DUK_LOCAL void duk__json_enc_array(duk_json_enc_ctx *js_ctx) { + duk_hthread *thr = js_ctx->thr; duk_idx_t entry_top; duk_idx_t idx_arr; duk_bool_t emitted; duk_uarridx_t i, arr_len; - DUK_DDD(DUK_DDDPRINT("duk__enc_array: array=%!T", - (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("duk__json_enc_array: array=%!T", + (duk_tval *) duk_get_tval(thr, -1))); - duk__enc_objarr_entry(js_ctx, &entry_top); + duk__json_enc_objarr_entry(js_ctx, &entry_top); idx_arr = entry_top - 1; @@ -30769,25 +38290,23 @@ DUK_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET); - arr_len = (duk_uarridx_t) duk_get_length(ctx, idx_arr); + arr_len = (duk_uarridx_t) duk_get_length(thr, idx_arr); emitted = 0; for (i = 0; i < arr_len; i++) { DUK_DDD(DUK_DDDPRINT("array entry loop: array=%!T, index=%ld, arr_len=%ld", - (duk_tval *) duk_get_tval(ctx, idx_arr), + (duk_tval *) duk_get_tval(thr, idx_arr), (long) i, (long) arr_len)); if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); } - /* XXX: duk_push_uint_string() */ - duk_push_uint(ctx, (duk_uint_t) i); - duk_to_string(ctx, -1); /* -> [ ... key ] */ + (void) duk_push_uint_to_hstring(thr, (duk_uint_t) i); /* -> [ ... key ] */ /* [ ... key ] */ - if (DUK_UNLIKELY(duk__enc_value(js_ctx, idx_arr) == 0)) { + if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_arr) == 0)) { /* Value would normally be omitted, replace with 'null'. */ DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); } else { @@ -30805,180 +38324,172 @@ DUK_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); } } DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET); - duk__enc_objarr_exit(js_ctx, &entry_top); + duk__json_enc_objarr_exit(js_ctx, &entry_top); - DUK_ASSERT_TOP(ctx, entry_top); + DUK_ASSERT_TOP(thr, entry_top); } /* The Str(key, holder) operation. * * Stack policy: [ ... key ] -> [ ... ] */ -DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder) { - duk_context *ctx = (duk_context *) js_ctx->thr; - duk_hthread *thr = (duk_hthread *) ctx; - duk_hobject *h_tmp; +DUK_LOCAL duk_bool_t duk__json_enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder) { + duk_hthread *thr = js_ctx->thr; duk_tval *tv; duk_tval *tv_holder; duk_tval *tv_key; duk_small_int_t c; - DUK_DDD(DUK_DDDPRINT("duk__enc_value: idx_holder=%ld, holder=%!T, key=%!T", - (long) idx_holder, (duk_tval *) duk_get_tval(ctx, idx_holder), - (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("duk__json_enc_value: idx_holder=%ld, holder=%!T, key=%!T", + (long) idx_holder, (duk_tval *) duk_get_tval(thr, idx_holder), + (duk_tval *) duk_get_tval(thr, -1))); - DUK_UNREF(thr); - - tv_holder = DUK_GET_TVAL_POSIDX(ctx, idx_holder); + tv_holder = DUK_GET_TVAL_POSIDX(thr, idx_holder); DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_holder)); - tv_key = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv_key = DUK_GET_TVAL_NEGIDX(thr, -1); DUK_ASSERT(DUK_TVAL_IS_STRING(tv_key)); + DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING(tv_key))); /* Caller responsible. */ (void) duk_hobject_getprop(thr, tv_holder, tv_key); /* -> [ ... key val ] */ - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - h_tmp = duk_get_hobject_or_lfunc_coerce(ctx, -1); - if (h_tmp != NULL) { - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_TO_JSON); - h_tmp = duk_get_hobject_or_lfunc_coerce(ctx, -1); /* toJSON() can also be a lightfunc */ - - if (h_tmp != NULL && DUK_HOBJECT_IS_CALLABLE(h_tmp)) { + /* Standard JSON checks for .toJSON() only for actual objects; for + * example, setting Number.prototype.toJSON and then serializing a + * number won't invoke the .toJSON() method. However, lightfuncs and + * plain buffers mimic objects so we check for their .toJSON() method. + */ + if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | + DUK_TYPE_MASK_LIGHTFUNC | + DUK_TYPE_MASK_BUFFER)) { + duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_JSON); + if (duk_is_callable(thr, -1)) { /* toJSON() can also be a lightfunc */ DUK_DDD(DUK_DDDPRINT("value is object, has callable toJSON() -> call it")); - /* XXX: duk_dup_unvalidated(ctx, -2) etc. */ - duk_dup(ctx, -2); /* -> [ ... key val toJSON val ] */ - duk_dup(ctx, -4); /* -> [ ... key val toJSON val key ] */ - duk_call_method(ctx, 1); /* -> [ ... key val val' ] */ - duk_remove(ctx, -2); /* -> [ ... key val' ] */ + /* XXX: duk_dup_unvalidated(thr, -2) etc. */ + duk_dup_m2(thr); /* -> [ ... key val toJSON val ] */ + duk_dup_m4(thr); /* -> [ ... key val toJSON val key ] */ + duk_call_method(thr, 1); /* -> [ ... key val val' ] */ + duk_remove_m2(thr); /* -> [ ... key val' ] */ } else { - duk_pop(ctx); /* -> [ ... key val ] */ + duk_pop(thr); /* -> [ ... key val ] */ } } /* [ ... key val ] */ - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); if (js_ctx->h_replacer) { /* XXX: Here a "slice copy" would be useful. */ DUK_DDD(DUK_DDDPRINT("replacer is set, call replacer")); - duk_push_hobject(ctx, js_ctx->h_replacer); /* -> [ ... key val replacer ] */ - duk_dup(ctx, idx_holder); /* -> [ ... key val replacer holder ] */ - duk_dup(ctx, -4); /* -> [ ... key val replacer holder key ] */ - duk_dup(ctx, -4); /* -> [ ... key val replacer holder key val ] */ - duk_call_method(ctx, 2); /* -> [ ... key val val' ] */ - duk_remove(ctx, -2); /* -> [ ... key val' ] */ + duk_push_hobject(thr, js_ctx->h_replacer); /* -> [ ... key val replacer ] */ + duk_dup(thr, idx_holder); /* -> [ ... key val replacer holder ] */ + duk_dup_m4(thr); /* -> [ ... key val replacer holder key ] */ + duk_dup_m4(thr); /* -> [ ... key val replacer holder key val ] */ + duk_call_method(thr, 2); /* -> [ ... key val val' ] */ + duk_remove_m2(thr); /* -> [ ... key val' ] */ } /* [ ... key val ] */ - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); if (DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h; h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) { +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) #if defined(DUK_USE_JX) || defined(DUK_USE_JC) - duk_hbufferobject *h_bufobj; - h_bufobj = (duk_hbufferobject *) h; - DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj); - - /* Conceptually we'd extract the plain underlying buffer - * or its slice and then do a type mask check below to - * see if we should reject it. Do the mask check here - * instead to avoid making a copy of the buffer slice. - */ - - if (js_ctx->mask_for_undefined & DUK_TYPE_MASK_BUFFER) { - DUK_DDD(DUK_DDDPRINT("-> bufferobject (-> plain buffer) will result in undefined (type mask check)")); - goto pop2_undef; - } - DUK_DDD(DUK_DDDPRINT("-> bufferobject won't result in undefined, encode directly")); - duk__enc_bufferobject(js_ctx, h_bufobj); + if (DUK_HOBJECT_IS_BUFOBJ(h) && + js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE)) { + /* With JX/JC a bufferobject gets serialized specially. */ + duk_hbufobj *h_bufobj; + h_bufobj = (duk_hbufobj *) h; + DUK_HBUFOBJ_ASSERT_VALID(h_bufobj); + duk__json_enc_bufobj(js_ctx, h_bufobj); goto pop2_emitted; -#else - DUK_DDD(DUK_DDDPRINT("no JX/JC support, bufferobject/buffer will always result in undefined")); - goto pop2_undef; -#endif - } else { - c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h); - switch ((int) c) { - case DUK_HOBJECT_CLASS_NUMBER: { - DUK_DDD(DUK_DDDPRINT("value is a Number object -> coerce with ToNumber()")); - duk_to_number(ctx, -1); - /* The coercion potentially invokes user .valueOf() and .toString() - * but can't result in a function value because [[DefaultValue]] would - * reject such a result: test-dev-json-stringify-coercion-1.js. - */ - DUK_ASSERT(!duk_is_callable(ctx, -1)); - break; - } - case DUK_HOBJECT_CLASS_STRING: { - DUK_DDD(DUK_DDDPRINT("value is a String object -> coerce with ToString()")); - duk_to_string(ctx, -1); - /* Same coercion behavior as for Number. */ - DUK_ASSERT(!duk_is_callable(ctx, -1)); - break; - } + } + /* Otherwise bufferobjects get serialized as normal objects. */ +#endif /* JX || JC */ +#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ + c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h); + switch (c) { + case DUK_HOBJECT_CLASS_NUMBER: { + DUK_DDD(DUK_DDDPRINT("value is a Number object -> coerce with ToNumber()")); + duk_to_number_m1(thr); + /* The coercion potentially invokes user .valueOf() and .toString() + * but can't result in a function value because ToPrimitive() would + * reject such a result: test-dev-json-stringify-coercion-1.js. + */ + DUK_ASSERT(!duk_is_callable(thr, -1)); + break; + } + case DUK_HOBJECT_CLASS_STRING: { + DUK_DDD(DUK_DDDPRINT("value is a String object -> coerce with ToString()")); + duk_to_string(thr, -1); + /* Same coercion behavior as for Number. */ + DUK_ASSERT(!duk_is_callable(thr, -1)); + break; + } #if defined(DUK_USE_JX) || defined(DUK_USE_JC) - case DUK_HOBJECT_CLASS_POINTER: + case DUK_HOBJECT_CLASS_POINTER: #endif - case DUK_HOBJECT_CLASS_BOOLEAN: { - DUK_DDD(DUK_DDDPRINT("value is a Boolean/Buffer/Pointer object -> get internal value")); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE); - duk_remove(ctx, -2); - break; - } - default: { - /* Normal object which doesn't get automatically coerced to a - * primitive value. Functions are checked for specially. The - * primitive value coercions for Number, String, Pointer, and - * Boolean can't result in functions so suffices to check here. - */ - DUK_ASSERT(h != NULL); - if (DUK_HOBJECT_IS_CALLABLE(h)) { + case DUK_HOBJECT_CLASS_BOOLEAN: { + DUK_DDD(DUK_DDDPRINT("value is a Boolean/Buffer/Pointer object -> get internal value")); + duk_xget_owndataprop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE); + duk_remove_m2(thr); + break; + } + default: { + /* Normal object which doesn't get automatically coerced to a + * primitive value. Functions are checked for specially. The + * primitive value coercions for Number, String, Pointer, and + * Boolean can't result in functions so suffices to check here. + * Symbol objects are handled like plain objects (their primitive + * value is NOT looked up like for e.g. String objects). + */ + DUK_ASSERT(h != NULL); + if (DUK_HOBJECT_IS_CALLABLE(h)) { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) - if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | - DUK_JSON_FLAG_EXT_COMPATIBLE)) { - /* We only get here when doing non-standard JSON encoding */ - DUK_DDD(DUK_DDDPRINT("-> function allowed, serialize to custom format")); - DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); - DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); - goto pop2_emitted; - } else { - DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)")); - goto pop2_undef; - } -#else /* DUK_USE_JX || DUK_USE_JC */ + if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | + DUK_JSON_FLAG_EXT_COMPATIBLE)) { + /* We only get here when doing non-standard JSON encoding */ + DUK_DDD(DUK_DDDPRINT("-> function allowed, serialize to custom format")); + DUK_ASSERT(js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible); + DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); + goto pop2_emitted; + } else { DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)")); goto pop2_undef; -#endif /* DUK_USE_JX || DUK_USE_JC */ } +#else /* DUK_USE_JX || DUK_USE_JC */ + DUK_DDD(DUK_DDDPRINT("-> will result in undefined (function)")); + goto pop2_undef; +#endif /* DUK_USE_JX || DUK_USE_JC */ } - } /* end switch */ } + } /* end switch */ } /* [ ... key val ] */ - DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - if (duk_check_type_mask(ctx, -1, js_ctx->mask_for_undefined)) { + if (duk_check_type_mask(thr, -1, js_ctx->mask_for_undefined)) { /* will result in undefined */ DUK_DDD(DUK_DDDPRINT("-> will result in undefined (type mask check)")); goto pop2_undef; } - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); switch (DUK_TVAL_GET_TAG(tv)) { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) @@ -31000,15 +38511,17 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold #if defined(DUK_USE_JX) || defined(DUK_USE_JC) /* When JX/JC not in use, the type mask above will avoid this case if needed. */ case DUK_TAG_POINTER: { - duk__enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); + duk__json_enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); break; } #endif /* DUK_USE_JX || DUK_USE_JC */ case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - - duk__enc_quote_string(js_ctx, h); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + goto pop2_undef; + } + duk__json_enc_quote_string(js_ctx, h); break; } case DUK_TAG_OBJECT: { @@ -31020,20 +38533,34 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold */ DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE(h)); - if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY) { - duk__enc_array(js_ctx); + if (duk_js_isarray_hobject(h)) { + duk__json_enc_array(js_ctx); } else { - duk__enc_object(js_ctx); + duk__json_enc_object(js_ctx); } break; } -#if defined(DUK_USE_JX) || defined(DUK_USE_JC) - /* When JX/JC not in use, the type mask above will avoid this case if needed. */ + /* Because plain buffers mimics Uint8Array, they have enumerable + * index properties [0,byteLength[. Because JSON only serializes + * enumerable own properties, no properties can be serialized for + * plain buffers (all virtual properties are non-enumerable). However, + * there may be a .toJSON() method which was already handled above. + */ case DUK_TAG_BUFFER: { - duk__enc_buffer(js_ctx, DUK_TVAL_GET_BUFFER(tv)); +#if defined(DUK_USE_JX) || defined(DUK_USE_JC) + if (js_ctx->flag_ext_custom_or_compatible) { + duk__json_enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + break; + } +#endif + + /* Could implement a fastpath, but the fast path would need + * to handle realloc side effects correctly. + */ + duk_to_object(thr, -1); + duk__json_enc_object(js_ctx); break; } -#endif /* DUK_USE_JX || DUK_USE_JC */ case DUK_TAG_LIGHTFUNC: { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) /* We only get here when doing non-standard JSON encoding */ @@ -31050,7 +38577,7 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold /* Number serialization has a significant impact relative to * other fast path code, so careful fast path for fastints. */ - duk__enc_fastint_tval(js_ctx, tv); + duk__json_enc_fastint_tval(js_ctx, tv); break; #endif default: { @@ -31060,29 +38587,42 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold /* XXX: A fast path for usual integers would be useful when * fastint support is not enabled. */ - duk__enc_double(js_ctx); + duk__json_enc_double(js_ctx); break; } } +#if defined(DUK_USE_JX) || defined(DUK_USE_JC) pop2_emitted: - duk_pop_2(ctx); /* [ ... key val ] -> [ ... ] */ +#endif + duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */ return 1; /* emitted */ pop2_undef: - duk_pop_2(ctx); /* [ ... key val ] -> [ ... ] */ + duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */ return 0; /* not emitted */ } /* E5 Section 15.12.3, main algorithm, step 4.b.ii steps 1-4. */ -DUK_LOCAL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv) { - duk_hobject *h; +DUK_LOCAL duk_bool_t duk__json_enc_allow_into_proplist(duk_tval *tv) { duk_small_int_t c; + /* XXX: some kind of external internal type checker? + * - type mask; symbol flag; class mask + */ DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_STRING(tv) || DUK_TVAL_IS_NUMBER(tv)) { + if (DUK_TVAL_IS_STRING(tv)) { + duk_hstring *h; + h = DUK_TVAL_GET_STRING(tv); + DUK_ASSERT(h != NULL); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + return 0; + } + return 1; + } else if (DUK_TVAL_IS_NUMBER(tv)) { return 1; } else if (DUK_TVAL_IS_OBJECT(tv)) { + duk_hobject *h; h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); c = (duk_small_int_t) DUK_HOBJECT_GET_CLASS_NUMBER(h); @@ -31142,10 +38682,12 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du } case DUK_TAG_STRING: { duk_hstring *h; - h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - duk__enc_quote_string(js_ctx, h); + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { + goto emit_undefined; + } + duk__json_enc_quote_string(js_ctx, h); break; } case DUK_TAG_OBJECT: { @@ -31153,7 +38695,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du duk_tval *tv_val; duk_bool_t emitted = 0; duk_uint32_t c_bit, c_all, c_array, c_unbox, c_undef, - c_func, c_bufobj, c_object; + c_func, c_bufobj, c_object, c_abort; /* For objects JSON.stringify() only looks for own, enumerable * properties which is nice for the fast path here. @@ -31180,23 +38722,25 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du obj = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(obj != NULL); - DUK_ASSERT_HOBJECT_VALID(obj); + DUK_HOBJECT_ASSERT_VALID(obj); /* Once recursion depth is increased, exit path must decrease * it (though it's OK to abort the fast path). */ - DUK_ASSERT(js_ctx->recursion_depth >= 0); + DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0); /* unsigned */ DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit); if (js_ctx->recursion_depth >= js_ctx->recursion_limit) { DUK_DD(DUK_DDPRINT("fast path recursion limit")); - DUK_ERROR_RANGE(js_ctx->thr, DUK_STR_JSONDEC_RECLIMIT); + DUK_ERROR_RANGE(js_ctx->thr, DUK_STR_DEC_RECLIMIT); + DUK_WO_NORETURN(return 0;); } for (i = 0, n = (duk_uint_fast32_t) js_ctx->recursion_depth; i < n; i++) { if (DUK_UNLIKELY(js_ctx->visiting[i] == obj)) { DUK_DD(DUK_DDPRINT("fast path loop detect")); DUK_ERROR_TYPE(js_ctx->thr, DUK_STR_CYCLIC_INPUT); + DUK_WO_NORETURN(return 0;); } } @@ -31218,7 +38762,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du * but does at the moment, probably not worth fixing. */ if (duk_hobject_hasprop_raw(js_ctx->thr, obj, DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr)) || - DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { + DUK_HOBJECT_IS_PROXY(obj)) { DUK_DD(DUK_DDPRINT("object has a .toJSON property or object is a Proxy, abort fast path")); goto abort_fastpath; } @@ -31239,11 +38783,12 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du c_unbox = DUK_HOBJECT_CMASK_NUMBER | DUK_HOBJECT_CMASK_STRING | DUK_HOBJECT_CMASK_BOOLEAN | - DUK_HOBJECT_CMASK_POINTER; + DUK_HOBJECT_CMASK_POINTER; /* Symbols are not unboxed. */ c_func = DUK_HOBJECT_CMASK_FUNCTION; - c_bufobj = DUK_HOBJECT_CMASK_ALL_BUFFEROBJECTS; + c_bufobj = DUK_HOBJECT_CMASK_ALL_BUFOBJS; c_undef = 0; - c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef); + c_abort = 0; + c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort); } else #endif @@ -31252,16 +38797,20 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du c_array = DUK_HOBJECT_CMASK_ARRAY; c_unbox = DUK_HOBJECT_CMASK_NUMBER | DUK_HOBJECT_CMASK_STRING | - DUK_HOBJECT_CMASK_BOOLEAN; + DUK_HOBJECT_CMASK_BOOLEAN; /* Symbols are not unboxed. */ c_func = 0; c_bufobj = 0; c_undef = DUK_HOBJECT_CMASK_FUNCTION | - DUK_HOBJECT_CMASK_POINTER | - DUK_HOBJECT_CMASK_ALL_BUFFEROBJECTS; - c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef); + DUK_HOBJECT_CMASK_POINTER; + /* As the fast path doesn't currently properly support + * duk_hbufobj virtual properties, abort fast path if + * we encounter them in plain JSON mode. + */ + c_abort = DUK_HOBJECT_CMASK_ALL_BUFOBJS; + c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort); } - c_bit = DUK_HOBJECT_GET_CLASS_MASK(obj); + c_bit = (duk_uint32_t) DUK_HOBJECT_GET_CLASS_MASK(obj); if (c_bit & c_object) { /* All other object types. */ DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY); @@ -31283,6 +38832,15 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du if (!k) { continue; } + if (DUK_HSTRING_HAS_ARRIDX(k)) { + /* If an object has array index keys we would need + * to sort them into the ES2015 enumeration order to + * be consistent with the slow path. Abort the fast + * path and handle in the slow path for now. + */ + DUK_DD(DUK_DDPRINT("property key is an array index, abort fast path")); + goto abort_fastpath; + } if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(js_ctx->thr->heap, obj, i)) { continue; } @@ -31293,7 +38851,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK_DD(DUK_DDPRINT("property is an accessor, abort fast path")); goto abort_fastpath; } - if (DUK_HSTRING_HAS_INTERNAL(k)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) { continue; } @@ -31301,11 +38859,11 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw); if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth); - duk__enc_key_autoquote(js_ctx, k); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); + duk__json_enc_key_autoquote(js_ctx, k); DUK__EMIT_2(js_ctx, DUK_ASC_COLON, DUK_ASC_SPACE); } else { - duk__enc_key_autoquote(js_ctx, k); + duk__json_enc_key_autoquote(js_ctx, k); DUK__EMIT_1(js_ctx, DUK_ASC_COLON); } @@ -31319,8 +38877,8 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du } /* If any non-Array value had enumerable virtual own - * properties, they should be serialized here. Standard - * types don't. + * properties, they should be serialized here (actually, + * before the explicit properties). Standard types don't. */ if (emitted) { @@ -31328,7 +38886,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); } } DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); @@ -31344,57 +38902,58 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du goto abort_fastpath; } - arr_len = (duk_uint_fast32_t) duk_hobject_get_length(js_ctx->thr, obj); + arr_len = (duk_uint_fast32_t) ((duk_harray *) obj)->length; asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj); - if (arr_len > asize) { - /* Array length is larger than 'asize'. This shouldn't - * happen in practice. Bail out just in case. - */ - DUK_DD(DUK_DDPRINT("arr_len > asize, abort fast path")); - goto abort_fastpath; - } /* Array part may be larger than 'length'; if so, iterate - * only up to array 'length'. + * only up to array 'length'. Array part may also be smaller + * than 'length' in some cases. */ for (i = 0; i < arr_len; i++) { - DUK_ASSERT(i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj)); - - tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i); + duk_tval *tv_arrval; + duk_hstring *h_tmp; + duk_bool_t has_inherited; if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth); } - if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_val))) { - /* Gap in array; check for inherited property, - * bail out if one exists. This should be enough - * to support gappy arrays for all practical code. - */ - duk_hstring *h_tmp; - duk_bool_t has_inherited; - - /* XXX: refactor into an internal helper, pretty awkward */ - duk_push_uint((duk_context *) js_ctx->thr, (duk_uint_t) i); - h_tmp = duk_to_hstring((duk_context *) js_ctx->thr, -1); - DUK_ASSERT(h_tmp != NULL); - has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp); - duk_pop((duk_context *) js_ctx->thr); - - if (has_inherited) { - DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path")); - goto abort_fastpath; - } - - /* Ordinary gap, undefined encodes to 'null' in - * standard JSON (and no JX/JC support here now). - */ - DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path")); - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); - } else { - if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) { - DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); + if (DUK_LIKELY(i < asize)) { + tv_arrval = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i); + if (DUK_LIKELY(!DUK_TVAL_IS_UNUSED(tv_arrval))) { + /* Expected case: element is present. */ + if (duk__json_stringify_fast_value(js_ctx, tv_arrval) == 0) { + DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); + } + goto elem_done; } } + + /* Gap in array; check for inherited property, + * bail out if one exists. This should be enough + * to support gappy arrays for all practical code. + */ + + h_tmp = duk_push_uint_to_hstring(js_ctx->thr, (duk_uint_t) i); + has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp); + duk_pop(js_ctx->thr); + if (has_inherited) { + DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path")); + goto abort_fastpath; + } + + /* Ordinary gap, undefined encodes to 'null' in + * standard JSON, but JX/JC use their form for + * undefined to better preserve the typing. + */ + DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path")); +#if defined(DUK_USE_JX) + DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_undefined); +#else + DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL); +#endif + /* fall through */ + + elem_done: DUK__EMIT_1(js_ctx, DUK_ASC_COMMA); emitted = 1; } @@ -31404,7 +38963,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { DUK_ASSERT(js_ctx->recursion_depth >= 1); - duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1); + duk__json_enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U); } } DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET); @@ -31413,6 +38972,8 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du * automatic unboxing. Rely on internal value being * sane (to avoid infinite recursion). */ + DUK_ASSERT((c_bit & DUK_HOBJECT_CMASK_SYMBOL) == 0); /* Symbols are not unboxed. */ + #if 1 /* The code below is incorrect if .toString() or .valueOf() have * have been overridden. The correct approach would be to look up @@ -31442,9 +39003,14 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du #if defined(DUK_USE_JX) || defined(DUK_USE_JC) } else if (c_bit & c_func) { DUK__EMIT_STRIDX(js_ctx, js_ctx->stridx_custom_function); +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (c_bit & c_bufobj) { - duk__enc_bufferobject(js_ctx, (duk_hbufferobject *) obj); + duk__json_enc_bufobj(js_ctx, (duk_hbufobj *) obj); #endif +#endif + } else if (c_bit & c_abort) { + DUK_DD(DUK_DDPRINT("abort fast path for unsupported type")); + goto abort_fastpath; } else { DUK_ASSERT((c_bit & c_undef) != 0); @@ -31461,21 +39027,39 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du break; } case DUK_TAG_BUFFER: { + /* Plain buffers are treated like Uint8Arrays: they have + * enumerable indices. Other virtual properties are not + * enumerable, and inherited properties are not serialized. + * However, there can be a replacer (not relevant here) or + * a .toJSON() method (which we need to check for explicitly). + */ + +#if defined(DUK_USE_BUFFEROBJECT_SUPPORT) + if (duk_hobject_hasprop_raw(js_ctx->thr, + js_ctx->thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE], + DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr))) { + DUK_DD(DUK_DDPRINT("value is a plain buffer and there's an inherited .toJSON, abort fast path")); + goto abort_fastpath; + } +#endif + #if defined(DUK_USE_JX) || defined(DUK_USE_JC) if (js_ctx->flag_ext_custom_or_compatible) { - duk__enc_buffer(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + duk__json_enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); break; - } else { - goto emit_undefined; } -#else - goto emit_undefined; #endif + + /* Plain buffers mimic Uint8Arrays, and have enumerable index + * properties. + */ + duk__json_enc_buffer_json_fastpath(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + break; } case DUK_TAG_POINTER: { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) if (js_ctx->flag_ext_custom_or_compatible) { - duk__enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); + duk__json_enc_pointer(js_ctx, DUK_TVAL_GET_POINTER(tv)); break; } else { goto emit_undefined; @@ -31497,7 +39081,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du /* Number serialization has a significant impact relative to * other fast path code, so careful fast path for fastints. */ - duk__enc_fastint_tval(js_ctx, tv); + duk__json_enc_fastint_tval(js_ctx, tv); break; } #endif @@ -31509,9 +39093,9 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); /* XXX: Stack discipline is annoying, could be changed in numconv. */ - duk_push_tval((duk_context *) js_ctx->thr, tv); - duk__enc_double(js_ctx); - duk_pop((duk_context *) js_ctx->thr); + duk_push_tval(js_ctx->thr, tv); + duk__json_enc_double(js_ctx); + duk_pop(js_ctx->thr); #if 0 /* Could also rely on native sprintf(), but it will handle @@ -31536,24 +39120,24 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du abort_fastpath: /* Error message doesn't matter: the error is ignored anyway. */ DUK_DD(DUK_DDPRINT("aborting fast path")); - DUK_ERROR_INTERNAL_DEFMSG(js_ctx->thr); - return 0; /* unreachable */ + DUK_ERROR_INTERNAL(js_ctx->thr); + DUK_WO_NORETURN(return 0;); } -DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_context *ctx) { +DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_hthread *thr, void *udata) { duk_json_enc_ctx *js_ctx; duk_tval *tv; - DUK_ASSERT(ctx != NULL); - tv = DUK_GET_TVAL_NEGIDX(ctx, -2); - DUK_ASSERT(DUK_TVAL_IS_POINTER(tv)); - js_ctx = (duk_json_enc_ctx *) DUK_TVAL_GET_POINTER(tv); + DUK_ASSERT(thr != NULL); + DUK_ASSERT(udata != NULL); + + js_ctx = (duk_json_enc_ctx *) udata; DUK_ASSERT(js_ctx != NULL); - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); if (duk__json_stringify_fast_value(js_ctx, tv) == 0) { DUK_DD(DUK_DDPRINT("top level value not supported, fail fast path")); - return DUK_RET_ERROR; /* error message doesn't matter, ignored anyway */ + DUK_DCERROR_TYPE_INVALID_ARGS(thr); /* Error message is ignored, so doesn't matter. */ } return 0; @@ -31565,16 +39149,15 @@ DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_context *ctx) { */ DUK_INTERNAL -void duk_bi_json_parse_helper(duk_context *ctx, +void duk_bi_json_parse_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_reviver, duk_small_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; duk_json_dec_ctx js_ctx_alloc; duk_json_dec_ctx *js_ctx = &js_ctx_alloc; duk_hstring *h_text; -#ifdef DUK_USE_ASSERTIONS - duk_idx_t entry_top = duk_get_top(ctx); +#if defined(DUK_USE_ASSERTIONS) + duk_idx_t entry_top = duk_get_top(thr); #endif /* negative top-relative indices not allowed now */ @@ -31582,14 +39165,14 @@ void duk_bi_json_parse_helper(duk_context *ctx, DUK_ASSERT(idx_reviver == DUK_INVALID_INDEX || idx_reviver >= 0); DUK_DDD(DUK_DDDPRINT("JSON parse start: text=%!T, reviver=%!T, flags=0x%08lx, stack_top=%ld", - (duk_tval *) duk_get_tval(ctx, idx_value), - (duk_tval *) duk_get_tval(ctx, idx_reviver), + (duk_tval *) duk_get_tval(thr, idx_value), + (duk_tval *) duk_get_tval(thr, idx_reviver), (unsigned long) flags, - (long) duk_get_top(ctx))); + (long) duk_get_top(thr))); - DUK_MEMZERO(&js_ctx_alloc, sizeof(js_ctx_alloc)); + duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc)); js_ctx->thr = thr; -#ifdef DUK_USE_EXPLICIT_NULL_INIT +#if defined(DUK_USE_EXPLICIT_NULL_INIT) /* nothing now */ #endif js_ctx->recursion_limit = DUK_USE_JSON_DEC_RECLIMIT; @@ -31610,7 +39193,7 @@ void duk_bi_json_parse_helper(duk_context *ctx, js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE); #endif - h_text = duk_to_hstring(ctx, idx_value); /* coerce in-place */ + h_text = duk_to_hstring(thr, idx_value); /* coerce in-place; rejects Symbols */ DUK_ASSERT(h_text != NULL); /* JSON parsing code is allowed to read [p_start,p_end]: p_end is @@ -31623,57 +39206,59 @@ void duk_bi_json_parse_helper(duk_context *ctx, DUK_HSTRING_GET_BYTELEN(h_text); DUK_ASSERT(*(js_ctx->p_end) == 0x00); - duk__dec_value(js_ctx); /* -> [ ... value ] */ + duk__json_dec_value(js_ctx); /* -> [ ... value ] */ + DUK_ASSERT(js_ctx->recursion_depth == 0); - /* Trailing whitespace has been eaten by duk__dec_value(), so if + /* Trailing whitespace has been eaten by duk__json_dec_value(), so if * we're not at end of input here, it's a SyntaxError. */ if (js_ctx->p != js_ctx->p_end) { - duk__dec_syntax_error(js_ctx); + duk__json_dec_syntax_error(js_ctx); } - if (duk_is_callable(ctx, idx_reviver)) { + if (duk_is_callable(thr, idx_reviver)) { DUK_DDD(DUK_DDDPRINT("applying reviver: %!T", - (duk_tval *) duk_get_tval(ctx, idx_reviver))); + (duk_tval *) duk_get_tval(thr, idx_reviver))); js_ctx->idx_reviver = idx_reviver; - duk_push_object(ctx); - duk_dup(ctx, -2); /* -> [ ... val root val ] */ - duk_put_prop_stridx(ctx, -2, DUK_STRIDX_EMPTY_STRING); /* default attrs ok */ - duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); /* -> [ ... val root "" ] */ + duk_push_object(thr); + duk_dup_m2(thr); /* -> [ ... val root val ] */ + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING); /* default attrs ok */ + duk_push_hstring_stridx(thr, DUK_STRIDX_EMPTY_STRING); /* -> [ ... val root "" ] */ DUK_DDD(DUK_DDDPRINT("start reviver walk, root=%!T, name=%!T", - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); - duk__dec_reviver_walk(js_ctx); /* [ ... val root "" ] -> [ ... val val' ] */ - duk_remove(ctx, -2); /* -> [ ... val' ] */ + DUK_ASSERT(js_ctx->recursion_depth == 0); + duk__json_dec_reviver_walk(js_ctx); /* [ ... val root "" ] -> [ ... val val' ] */ + DUK_ASSERT(js_ctx->recursion_depth == 0); + duk_remove_m2(thr); /* -> [ ... val' ] */ } else { DUK_DDD(DUK_DDDPRINT("reviver does not exist or is not callable: %!T", - (duk_tval *) duk_get_tval(ctx, idx_reviver))); + (duk_tval *) duk_get_tval(thr, idx_reviver))); } /* Final result is at stack top. */ DUK_DDD(DUK_DDDPRINT("JSON parse end: text=%!T, reviver=%!T, flags=0x%08lx, result=%!T, stack_top=%ld", - (duk_tval *) duk_get_tval(ctx, idx_value), - (duk_tval *) duk_get_tval(ctx, idx_reviver), + (duk_tval *) duk_get_tval(thr, idx_value), + (duk_tval *) duk_get_tval(thr, idx_reviver), (unsigned long) flags, - (duk_tval *) duk_get_tval(ctx, -1), - (long) duk_get_top(ctx))); + (duk_tval *) duk_get_tval(thr, -1), + (long) duk_get_top(thr))); - DUK_ASSERT(duk_get_top(ctx) == entry_top + 1); + DUK_ASSERT(duk_get_top(thr) == entry_top + 1); } DUK_INTERNAL -void duk_bi_json_stringify_helper(duk_context *ctx, +void duk_bi_json_stringify_helper(duk_hthread *thr, duk_idx_t idx_value, duk_idx_t idx_replacer, duk_idx_t idx_space, duk_small_uint_t flags) { - duk_hthread *thr = (duk_hthread *) ctx; duk_json_enc_ctx js_ctx_alloc; duk_json_enc_ctx *js_ctx = &js_ctx_alloc; duk_hobject *h; @@ -31686,21 +39271,21 @@ void duk_bi_json_stringify_helper(duk_context *ctx, DUK_ASSERT(idx_space == DUK_INVALID_INDEX || idx_space >= 0); DUK_DDD(DUK_DDDPRINT("JSON stringify start: value=%!T, replacer=%!T, space=%!T, flags=0x%08lx, stack_top=%ld", - (duk_tval *) duk_get_tval(ctx, idx_value), - (duk_tval *) duk_get_tval(ctx, idx_replacer), - (duk_tval *) duk_get_tval(ctx, idx_space), + (duk_tval *) duk_get_tval(thr, idx_value), + (duk_tval *) duk_get_tval(thr, idx_replacer), + (duk_tval *) duk_get_tval(thr, idx_space), (unsigned long) flags, - (long) duk_get_top(ctx))); + (long) duk_get_top(thr))); - entry_top = duk_get_top(ctx); + entry_top = duk_get_top(thr); /* * Context init */ - DUK_MEMZERO(&js_ctx_alloc, sizeof(js_ctx_alloc)); + duk_memzero(&js_ctx_alloc, sizeof(js_ctx_alloc)); js_ctx->thr = thr; -#ifdef DUK_USE_EXPLICIT_NULL_INIT +#if defined(DUK_USE_EXPLICIT_NULL_INIT) js_ctx->h_replacer = NULL; js_ctx->h_gap = NULL; #endif @@ -31713,20 +39298,21 @@ void duk_bi_json_stringify_helper(duk_context *ctx, js_ctx->flags = flags; js_ctx->flag_ascii_only = flags & DUK_JSON_FLAG_ASCII_ONLY; js_ctx->flag_avoid_key_quotes = flags & DUK_JSON_FLAG_AVOID_KEY_QUOTES; -#ifdef DUK_USE_JX +#if defined(DUK_USE_JX) js_ctx->flag_ext_custom = flags & DUK_JSON_FLAG_EXT_CUSTOM; #endif -#ifdef DUK_USE_JC +#if defined(DUK_USE_JC) js_ctx->flag_ext_compatible = flags & DUK_JSON_FLAG_EXT_COMPATIBLE; #endif #if defined(DUK_USE_JX) || defined(DUK_USE_JC) js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE); #endif - /* The #ifdef clutter here handles the JX/JC enable/disable + /* The #if defined() clutter here handles the JX/JC enable/disable * combinations properly. */ #if defined(DUK_USE_JX) || defined(DUK_USE_JC) + js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_NULL; /* standard JSON; array gaps */ #if defined(DUK_USE_JX) if (flags & DUK_JSON_FLAG_EXT_CUSTOM) { js_ctx->stridx_custom_undefined = DUK_STRIDX_LC_UNDEFINED; @@ -31761,15 +39347,19 @@ void duk_bi_json_stringify_helper(duk_context *ctx, else #endif /* DUK_USE_JX || DUK_USE_JC */ { + /* Plain buffer is treated like ArrayBuffer and serialized. + * Lightfuncs are treated like objects, but JSON explicitly + * skips serializing Function objects so we can just reject + * lightfuncs here. + */ js_ctx->mask_for_undefined = DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_POINTER | - DUK_TYPE_MASK_BUFFER | DUK_TYPE_MASK_LIGHTFUNC; } DUK_BW_INIT_PUSHBUF(thr, &js_ctx->bw, DUK__JSON_STRINGIFY_BUFSIZE); - js_ctx->idx_loop = duk_push_object_internal(ctx); + js_ctx->idx_loop = duk_push_bare_object(thr); DUK_ASSERT(js_ctx->idx_loop >= 0); /* [ ... buf loop ] */ @@ -31778,11 +39368,11 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * Process replacer/proplist (2nd argument to JSON.stringify) */ - h = duk_get_hobject(ctx, idx_replacer); + h = duk_get_hobject(thr, idx_replacer); if (h != NULL) { if (DUK_HOBJECT_IS_CALLABLE(h)) { js_ctx->h_replacer = h; - } else if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY) { + } else if (duk_js_isarray_hobject(h)) { /* Here the specification requires correct array index enumeration * which is a bit tricky for sparse arrays (it is handled by the * enum setup code). We now enumerate ancestors too, although the @@ -31792,30 +39382,30 @@ void duk_bi_json_stringify_helper(duk_context *ctx, duk_uarridx_t plist_idx = 0; duk_small_uint_t enum_flags; - js_ctx->idx_proplist = duk_push_array(ctx); /* XXX: array internal? */ + js_ctx->idx_proplist = duk_push_bare_array(thr); enum_flags = DUK_ENUM_ARRAY_INDICES_ONLY | DUK_ENUM_SORT_ARRAY_INDICES; /* expensive flag */ - duk_enum(ctx, idx_replacer, enum_flags); - while (duk_next(ctx, -1 /*enum_index*/, 1 /*get_value*/)) { + duk_enum(thr, idx_replacer, enum_flags); + while (duk_next(thr, -1 /*enum_index*/, 1 /*get_value*/)) { /* [ ... proplist enum_obj key val ] */ - if (duk__enc_allow_into_proplist(duk_get_tval(ctx, -1))) { + if (duk__json_enc_allow_into_proplist(duk_get_tval(thr, -1))) { /* XXX: duplicates should be eliminated here */ DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> accept", - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); - duk_to_string(ctx, -1); /* extra coercion of strings is OK */ - duk_put_prop_index(ctx, -4, plist_idx); /* -> [ ... proplist enum_obj key ] */ + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); + duk_to_string(thr, -1); /* extra coercion of strings is OK */ + duk_put_prop_index(thr, -4, plist_idx); /* -> [ ... proplist enum_obj key ] */ plist_idx++; - duk_pop(ctx); + duk_pop(thr); } else { DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> reject", - (duk_tval *) duk_get_tval(ctx, -2), - (duk_tval *) duk_get_tval(ctx, -1))); - duk_pop_2(ctx); + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); + duk_pop_2(thr); } } - duk_pop(ctx); /* pop enum */ + duk_pop(thr); /* pop enum */ /* [ ... proplist ] */ } @@ -31827,17 +39417,17 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * Process space (3rd argument to JSON.stringify) */ - h = duk_get_hobject(ctx, idx_space); + h = duk_get_hobject(thr, idx_space); if (h != NULL) { - int c = DUK_HOBJECT_GET_CLASS_NUMBER(h); + duk_small_uint_t c = DUK_HOBJECT_GET_CLASS_NUMBER(h); if (c == DUK_HOBJECT_CLASS_NUMBER) { - duk_to_number(ctx, idx_space); + duk_to_number(thr, idx_space); } else if (c == DUK_HOBJECT_CLASS_STRING) { - duk_to_string(ctx, idx_space); + duk_to_string(thr, idx_space); } } - if (duk_is_number(ctx, idx_space)) { + if (duk_is_number(thr, idx_space)) { duk_small_int_t nspace; /* spaces[] must be static to allow initializer with old compilers like BCC */ static const char spaces[10] = { @@ -31847,25 +39437,26 @@ void duk_bi_json_stringify_helper(duk_context *ctx, }; /* XXX: helper */ /* ToInteger() coercion; NaN -> 0, infinities are clamped to 0 and 10 */ - nspace = (duk_small_int_t) duk_to_int_clamped(ctx, idx_space, 0 /*minval*/, 10 /*maxval*/); + nspace = (duk_small_int_t) duk_to_int_clamped(thr, idx_space, 0 /*minval*/, 10 /*maxval*/); DUK_ASSERT(nspace >= 0 && nspace <= 10); - duk_push_lstring(ctx, spaces, (duk_size_t) nspace); - js_ctx->h_gap = duk_get_hstring(ctx, -1); - DUK_ASSERT(js_ctx->h_gap != NULL); - } else if (duk_is_string(ctx, idx_space)) { - /* XXX: substring in-place at idx_place? */ - duk_dup(ctx, idx_space); - duk_substring(ctx, -1, 0, 10); /* clamp to 10 chars */ - js_ctx->h_gap = duk_get_hstring(ctx, -1); + duk_push_lstring(thr, spaces, (duk_size_t) nspace); + js_ctx->h_gap = duk_known_hstring(thr, -1); DUK_ASSERT(js_ctx->h_gap != NULL); + } else if (duk_is_string_notsymbol(thr, idx_space)) { + duk_dup(thr, idx_space); + duk_substring(thr, -1, 0, 10); /* clamp to 10 chars */ + js_ctx->h_gap = duk_known_hstring(thr, -1); } else { /* nop */ } if (js_ctx->h_gap != NULL) { - /* if gap is empty, behave as if not given at all */ - if (DUK_HSTRING_GET_CHARLEN(js_ctx->h_gap) == 0) { + /* If gap is empty, behave as if not given at all. Check + * against byte length because character length is more + * expensive. + */ + if (DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) == 0) { js_ctx->h_gap = NULL; } } @@ -31881,9 +39472,7 @@ void duk_bi_json_stringify_helper(duk_context *ctx, if (js_ctx->h_replacer == NULL && /* replacer is a mutation risk */ js_ctx->idx_proplist == -1) { /* proplist is very rare */ duk_int_t pcall_rc; -#ifdef DUK_USE_MARK_AND_SWEEP - duk_small_uint_t prev_mark_and_sweep_base_flags; -#endif + duk_small_uint_t prev_ms_base_flags; DUK_DD(DUK_DDPRINT("try JSON.stringify() fast path")); @@ -31902,22 +39491,21 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * limited loop detection). */ - duk_push_pointer(ctx, (void *) js_ctx); - duk_dup(ctx, idx_value); + duk_dup(thr, idx_value); -#if defined(DUK_USE_MARK_AND_SWEEP) /* Must prevent finalizers which may have arbitrary side effects. */ - prev_mark_and_sweep_base_flags = thr->heap->mark_and_sweep_base_flags; - thr->heap->mark_and_sweep_base_flags |= - DUK_MS_FLAG_NO_FINALIZERS | /* avoid attempts to add/remove object keys */ - DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid attempt to compact any objects */ -#endif + prev_ms_base_flags = thr->heap->ms_base_flags; + thr->heap->ms_base_flags |= + DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* Avoid attempt to compact any objects. */ + thr->heap->pf_prevent_count++; /* Prevent finalizers. */ + DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ - pcall_rc = duk_safe_call(ctx, duk__json_stringify_fast, 2 /*nargs*/, 0 /*nret*/); + pcall_rc = duk_safe_call(thr, duk__json_stringify_fast, (void *) js_ctx /*udata*/, 1 /*nargs*/, 0 /*nret*/); + + DUK_ASSERT(thr->heap->pf_prevent_count > 0); + thr->heap->pf_prevent_count--; + thr->heap->ms_base_flags = prev_ms_base_flags; -#if defined(DUK_USE_MARK_AND_SWEEP) - thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; -#endif if (pcall_rc == DUK_EXEC_SUCCESS) { DUK_DD(DUK_DDPRINT("fast path successful")); DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw); @@ -31939,31 +39527,31 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * Create wrapper object and serialize */ - idx_holder = duk_push_object(ctx); - duk_dup(ctx, idx_value); - duk_put_prop_stridx(ctx, -2, DUK_STRIDX_EMPTY_STRING); + idx_holder = duk_push_object(thr); + duk_dup(thr, idx_value); + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING); DUK_DDD(DUK_DDDPRINT("before: flags=0x%08lx, loop=%!T, replacer=%!O, " "proplist=%!T, gap=%!O, holder=%!T", (unsigned long) js_ctx->flags, - (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop), + (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop), (duk_heaphdr *) js_ctx->h_replacer, - (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(ctx, js_ctx->idx_proplist) : NULL), + (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL), (duk_heaphdr *) js_ctx->h_gap, - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -1))); /* serialize the wrapper with empty string key */ - duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING); + duk_push_hstring_empty(thr); /* [ ... buf loop (proplist) (gap) holder "" ] */ js_ctx->recursion_limit = DUK_USE_JSON_ENC_RECLIMIT; DUK_ASSERT(js_ctx->recursion_depth == 0); - if (DUK_UNLIKELY(duk__enc_value(js_ctx, idx_holder) == 0)) { /* [ ... holder key ] -> [ ... holder ] */ + if (DUK_UNLIKELY(duk__json_enc_value(js_ctx, idx_holder) == 0)) { /* [ ... holder key ] -> [ ... holder ] */ /* Result is undefined. */ - duk_push_undefined(ctx); + duk_push_undefined(thr); } else { /* Convert buffer to result string. */ DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw); @@ -31972,11 +39560,11 @@ void duk_bi_json_stringify_helper(duk_context *ctx, DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, " "proplist=%!T, gap=%!O, holder=%!T", (unsigned long) js_ctx->flags, - (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop), + (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop), (duk_heaphdr *) js_ctx->h_replacer, - (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(ctx, js_ctx->idx_proplist) : NULL), + (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL), (duk_heaphdr *) js_ctx->h_gap, - (duk_tval *) duk_get_tval(ctx, idx_holder))); + (duk_tval *) duk_get_tval(thr, idx_holder))); /* The stack has a variable shape here, so force it to the * desired one explicitly. @@ -31985,35 +39573,37 @@ void duk_bi_json_stringify_helper(duk_context *ctx, #if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) replace_finished: #endif - duk_replace(ctx, entry_top); - duk_set_top(ctx, entry_top + 1); + duk_replace(thr, entry_top); + duk_set_top(thr, entry_top + 1); DUK_DDD(DUK_DDDPRINT("JSON stringify end: value=%!T, replacer=%!T, space=%!T, " "flags=0x%08lx, result=%!T, stack_top=%ld", - (duk_tval *) duk_get_tval(ctx, idx_value), - (duk_tval *) duk_get_tval(ctx, idx_replacer), - (duk_tval *) duk_get_tval(ctx, idx_space), + (duk_tval *) duk_get_tval(thr, idx_value), + (duk_tval *) duk_get_tval(thr, idx_replacer), + (duk_tval *) duk_get_tval(thr, idx_space), (unsigned long) flags, - (duk_tval *) duk_get_tval(ctx, -1), - (long) duk_get_top(ctx))); + (duk_tval *) duk_get_tval(thr, -1), + (long) duk_get_top(thr))); - DUK_ASSERT(duk_get_top(ctx) == entry_top + 1); + DUK_ASSERT(duk_get_top(thr) == entry_top + 1); } +#if defined(DUK_USE_JSON_BUILTIN) + /* * Entry points */ -DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_context *ctx) { - duk_bi_json_parse_helper(ctx, +DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_hthread *thr) { + duk_bi_json_parse_helper(thr, 0 /*idx_value*/, 1 /*idx_replacer*/, 0 /*flags*/); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx) { - duk_bi_json_stringify_helper(ctx, +DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_hthread *thr) { + duk_bi_json_stringify_helper(thr, 0 /*idx_value*/, 1 /*idx_replacer*/, 2 /*idx_space*/, @@ -32021,318 +39611,28 @@ DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx) { return 1; } +#endif /* DUK_USE_JSON_BUILTIN */ + +#endif /* DUK_USE_JSON_SUPPORT */ + +/* automatic undefs */ +#undef DUK__EMIT_1 +#undef DUK__EMIT_2 +#undef DUK__EMIT_CSTR +#undef DUK__EMIT_HSTR +#undef DUK__EMIT_STRIDX #undef DUK__JSON_DECSTR_BUFSIZE #undef DUK__JSON_DECSTR_CHUNKSIZE #undef DUK__JSON_ENCSTR_CHUNKSIZE -#undef DUK__JSON_STRINGIFY_BUFSIZE #undef DUK__JSON_MAX_ESC_LEN -#line 1 "duk_bi_logger.c" -/* - * Logging support - */ - -/* include removed: duk_internal.h */ - -/* 3-letter log level strings */ -DUK_LOCAL const duk_uint8_t duk__log_level_strings[] = { - (duk_uint8_t) DUK_ASC_UC_T, (duk_uint8_t) DUK_ASC_UC_R, (duk_uint8_t) DUK_ASC_UC_C, - (duk_uint8_t) DUK_ASC_UC_D, (duk_uint8_t) DUK_ASC_UC_B, (duk_uint8_t) DUK_ASC_UC_G, - (duk_uint8_t) DUK_ASC_UC_I, (duk_uint8_t) DUK_ASC_UC_N, (duk_uint8_t) DUK_ASC_UC_F, - (duk_uint8_t) DUK_ASC_UC_W, (duk_uint8_t) DUK_ASC_UC_R, (duk_uint8_t) DUK_ASC_UC_N, - (duk_uint8_t) DUK_ASC_UC_E, (duk_uint8_t) DUK_ASC_UC_R, (duk_uint8_t) DUK_ASC_UC_R, - (duk_uint8_t) DUK_ASC_UC_F, (duk_uint8_t) DUK_ASC_UC_T, (duk_uint8_t) DUK_ASC_UC_L -}; - -/* Constructor */ -DUK_INTERNAL duk_ret_t duk_bi_logger_constructor(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_idx_t nargs; - - /* Calling as a non-constructor is not meaningful. */ - if (!duk_is_constructor_call(ctx)) { - return DUK_RET_TYPE_ERROR; - } - - nargs = duk_get_top(ctx); - duk_set_top(ctx, 1); - - duk_push_this(ctx); - - /* [ name this ] */ - - if (nargs == 0) { - /* Automatic defaulting of logger name from caller. This would - * work poorly with tail calls, but constructor calls are currently - * never tail calls, so tail calls are not an issue now. - */ - - if (thr->callstack_top >= 2) { - duk_activation *act_caller = thr->callstack + thr->callstack_top - 2; - duk_hobject *func_caller; - - func_caller = DUK_ACT_GET_FUNC(act_caller); - if (func_caller) { - /* Stripping the filename might be a good idea - * ("/foo/bar/quux.js" -> logger name "quux"), - * but now used verbatim. - */ - duk_push_hobject(ctx, func_caller); - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_FILE_NAME); - duk_replace(ctx, 0); - } - } - } - /* the stack is unbalanced here on purpose; we only rely on the - * initial two values: [ name this ]. - */ - - if (duk_is_string(ctx, 0)) { - duk_dup(ctx, 0); - duk_put_prop_stridx(ctx, 1, DUK_STRIDX_LC_N); - } else { - /* don't set 'n' at all, inherited value is used as name */ - } - - duk_compact(ctx, 1); - - return 0; /* keep default instance */ -} - -/* Default function to format objects. Tries to use toLogString() but falls - * back to toString(). Any errors are propagated out without catching. - */ -DUK_INTERNAL duk_ret_t duk_bi_logger_prototype_fmt(duk_context *ctx) { - if (duk_get_prop_stridx(ctx, 0, DUK_STRIDX_TO_LOG_STRING)) { - /* [ arg toLogString ] */ - - duk_dup(ctx, 0); - duk_call_method(ctx, 0); - - /* [ arg result ] */ - return 1; - } - - /* [ arg undefined ] */ - duk_pop(ctx); - duk_to_string(ctx, 0); - return 1; -} - -/* Default function to write a formatted log line. Writes to stderr, - * appending a newline to the log line. - * - * The argument is a buffer whose visible size contains the log message. - * This function should avoid coercing the buffer to a string to avoid - * string table traffic. - */ -DUK_INTERNAL duk_ret_t duk_bi_logger_prototype_raw(duk_context *ctx) { - const char *data; - duk_size_t data_len; - - DUK_UNREF(ctx); - DUK_UNREF(data); - DUK_UNREF(data_len); - -#ifdef DUK_USE_FILE_IO - data = (const char *) duk_require_buffer(ctx, 0, &data_len); - DUK_FWRITE((const void *) data, 1, data_len, DUK_STDERR); - DUK_FPUTC((int) '\n', DUK_STDERR); - DUK_FFLUSH(DUK_STDERR); -#else - /* nop */ -#endif - return 0; -} - -/* Log frontend shared helper, magic value indicates log level. Provides - * frontend functions: trace(), debug(), info(), warn(), error(), fatal(). - * This needs to have small footprint, reasonable performance, minimal - * memory churn, etc. - */ -DUK_INTERNAL duk_ret_t duk_bi_logger_prototype_log_shared(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_double_t now; - duk_small_int_t entry_lev = duk_get_current_magic(ctx); - duk_small_int_t logger_lev; - duk_int_t nargs; - duk_int_t i; - duk_size_t tot_len; - const duk_uint8_t *arg_str; - duk_size_t arg_len; - duk_uint8_t *buf, *p; - const duk_uint8_t *q; - duk_uint8_t date_buf[DUK_BI_DATE_ISO8601_BUFSIZE]; - duk_size_t date_len; - duk_small_int_t rc; - - DUK_ASSERT(entry_lev >= 0 && entry_lev <= 5); - DUK_UNREF(thr); - - /* XXX: sanitize to printable (and maybe ASCII) */ - /* XXX: better multiline */ - - /* - * Logger arguments are: - * - * magic: log level (0-5) - * this: logger - * stack: plain log args - * - * We want to minimize memory churn so a two-pass approach - * is used: first pass formats arguments and computes final - * string length, second pass copies strings either into a - * pre-allocated and reused buffer (short messages) or into a - * newly allocated fixed buffer. If the backend function plays - * nice, it won't coerce the buffer to a string (and thus - * intern it). - */ - - nargs = duk_get_top(ctx); - - /* [ arg1 ... argN this ] */ - - /* - * Log level check - */ - - duk_push_this(ctx); - - duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LC_L); - logger_lev = (duk_small_int_t) duk_get_int(ctx, -1); - if (entry_lev < logger_lev) { - return 0; - } - /* log level could be popped but that's not necessary */ - - now = DUK_USE_DATE_GET_NOW(ctx); - duk_bi_date_format_timeval(now, date_buf); - date_len = DUK_STRLEN((const char *) date_buf); - - duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LC_N); - duk_to_string(ctx, -1); - DUK_ASSERT(duk_is_string(ctx, -1)); - - /* [ arg1 ... argN this loggerLevel loggerName ] */ - - /* - * Pass 1 - */ - - /* Line format: